From b7d4f5308f226396f8da25ce1171ff971e300236 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 14:20:56 +0200 Subject: [PATCH 001/102] build(deps): bump slackapi/slack-github-action from 1.25.0 to 1.26.0 (#1803) Bumps [slackapi/slack-github-action](https://github.com/slackapi/slack-github-action) from 1.25.0 to 1.26.0. - [Release notes](https://github.com/slackapi/slack-github-action/releases) - [Commits](https://github.com/slackapi/slack-github-action/compare/v1.25.0...v1.26.0) --- updated-dependencies: - dependency-name: slackapi/slack-github-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/nightly-e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightly-e2e.yml b/.github/workflows/nightly-e2e.yml index 3e8cc7d614..7adbddf835 100644 --- a/.github/workflows/nightly-e2e.yml +++ b/.github/workflows/nightly-e2e.yml @@ -197,7 +197,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Notify Slack on failure - uses: slackapi/slack-github-action@v1.25.0 + uses: slackapi/slack-github-action@v1.26.0 env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK From 6f70509dd54885e8a708870fa5122927a2a37172 Mon Sep 17 00:00:00 2001 From: Marius Poke Date: Tue, 23 Apr 2024 14:42:30 +0200 Subject: [PATCH 002/102] chore: add v4.1.x to docs and cleanup bots (#1812) * add v4.1.x to releases and features * cleanup mergify and dependabot --- .github/dependabot.yml | 30 ------------------------------ .mergify.yml | 24 ------------------------ FEATURES.md | 31 ++++++++++++++++--------------- RELEASES.md | 14 +++++++++----- 4 files changed, 25 insertions(+), 74 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e588557500..a5e6e0588b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -17,36 +17,6 @@ updates: open-pull-requests-limit: 10 labels: - dependencies - - - package-ecosystem: gomod - directory: "/" - schedule: - interval: daily - target-branch: "release/v2.0.x" - # Only allow automated security-related dependency updates on release branches. - open-pull-requests-limit: 0 - labels: - - dependencies - - - package-ecosystem: gomod - directory: "/" - schedule: - interval: daily - target-branch: "release/v2.4.x-lsm" - # Only allow automated security-related dependency updates on release branches. - open-pull-requests-limit: 0 - labels: - - dependencies - - - package-ecosystem: gomod - directory: "/" - schedule: - interval: daily - target-branch: "release/v3.1.x" - # Only allow automated security-related dependency updates on release branches. - open-pull-requests-limit: 0 - labels: - - dependencies - package-ecosystem: gomod directory: "/" diff --git a/.mergify.yml b/.mergify.yml index 1c0a6bb81a..bdad9f819e 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -10,30 +10,6 @@ queue_rules: - "#approved-reviews-by>1" pull_request_rules: - - name: Backport patches to the release/v2.0.x branch - conditions: - - base=main - - label=A:backport/v2.0.x - actions: - backport: - branches: - - release/v2.0.x - - name: Backport patches to the release/v2.4.x-lsm branch - conditions: - - base=main - - label=A:backport/v2.4.x-lsm - actions: - backport: - branches: - - release/v2.4.x-lsm - - name: Backport patches to the release/v3.1.x branch - conditions: - - base=main - - label=A:backport/v3.1.x - actions: - backport: - branches: - - release/v3.1.x - name: Backport patches to the release/v3.2.x branch conditions: - base=main diff --git a/FEATURES.md b/FEATURES.md index c5d87df857..f9a54e0589 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -2,18 +2,19 @@ The following table indicates the major ICS features available in the [currently active releases](./RELEASES.md#version-matrix): -| Feature | `v3.2.0` | `v3.3.0` | `v3.3.3-lsm` | `v4.0.0` | -|---------|---------:|---------:|-------------:|---------:| -| [Channel initialization: new chains](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#channel-initialization-new-chains) | ✅ | ✅ | ✅ | ✅ | -| [Validator set update](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#validator-set-update) | ✅ | ✅ | ✅ | ✅ | -| [Completion of unbonding operations](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#completion-of-unbonding-operations) | ✅ | ✅ | ✅ | ✅ | -| [Consumer initiated slashing](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#consumer-initiated-slashing) | ✅ | ✅ | ✅ | ✅ | -| [Reward distribution](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#reward-distribution) | ✅ | ✅ | ✅ | ✅ | -| [Consumer chain removal](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/methods.md#consumer-chain-removal) | ✅ | ✅ | ✅ | ✅ | -| [Key assignment](https://github.com/cosmos/interchain-security/issues/26) | ✅ | ✅ | ✅ | ✅ | -| [Jail throttling](https://github.com/cosmos/interchain-security/issues/404) | ✅ | ✅ | ✅ | ✅ | -| [Soft opt-out](https://github.com/cosmos/interchain-security/issues/851) | ✅ | ✅ | ✅ | ✅ | -| [Channel initialization: existing chains](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#channel-initialization-existing-chains) (aka [Standalone to consumer changeover](https://github.com/cosmos/interchain-security/issues/756)) | ✅ | ✅ | ✅ | ✅ | -| [Cryptographic verification of equivocation](https://github.com/cosmos/interchain-security/issues/732) | ❌ | ✅ | ✅ | ✅ | -| [Jail throttling with retries](https://github.com/cosmos/interchain-security/issues/713) - consumer-side changes | ✅ | ✅ | ✅ | ✅ | -| [Jail throttling with retries](https://github.com/cosmos/interchain-security/issues/713) - [provider-side changes](https://github.com/cosmos/interchain-security/issues/1102) | ❌ | ❌ | ❌ | ✅ | +| Feature | `v3.2.0` | `v3.3.0` | `v3.3.3-lsm` | `v4.0.0` | `v4.1.1` | `v4.1.1-lsm` | +|---------|---------:|---------:|-------------:|---------:|---------:|-------------:| +| [Channel initialization: new chains](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#channel-initialization-new-chains) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Validator set update](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#validator-set-update) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Completion of unbonding operations](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#completion-of-unbonding-operations) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Consumer initiated slashing](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#consumer-initiated-slashing) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Reward distribution](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#reward-distribution) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Consumer chain removal](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/methods.md#consumer-chain-removal) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Key assignment](https://github.com/cosmos/interchain-security/issues/26) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Jail throttling](https://github.com/cosmos/interchain-security/issues/404) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Soft opt-out](https://github.com/cosmos/interchain-security/issues/851) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Channel initialization: existing chains](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#channel-initialization-existing-chains) (aka [Standalone to consumer changeover](https://github.com/cosmos/interchain-security/issues/756)) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Cryptographic verification of equivocation](https://github.com/cosmos/interchain-security/issues/732) | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Jail throttling with retries](https://github.com/cosmos/interchain-security/issues/713) - consumer-side changes | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Jail throttling with retries](https://github.com/cosmos/interchain-security/issues/713) - [provider-side changes](https://github.com/cosmos/interchain-security/issues/1102) | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | +| [ICS epochs](https://cosmos.github.io/interchain-security/adrs/adr-014-epochs) | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | diff --git a/RELEASES.md b/RELEASES.md index 41a2df9cfc..b1d3eefa22 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -63,6 +63,7 @@ All missing minor release versions have been discontinued. | `v3.3.x` | July 10, 2024 | | `v3.3.x-lsm` | July 10, 2024 | | `v4.0.x` | January 24, 2025 | +| `v4.1.x` | January 24, 2025 | **Note**: As of [Gaia v15.1.0](https://github.com/cosmos/gaia/releases/tag/v15.1.0), the Cosmos Hub uses a fork of Cosmos SDK ([v0.47.10-ics-lsm](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.47.10-ics-lsm)) @@ -81,6 +82,8 @@ Versions of Golang, IBC, Cosmos SDK and CometBFT used by ICS in the currently ac | [v3.3.0](https://github.com/cosmos/interchain-security/releases/tag/v3.3.0) | 1.20 | v7.3.0 | v0.47.5 | v0.37.2 | | [v3.3.3-lsm](https://github.com/cosmos/interchain-security/releases/tag/v3.3.3-lsm) | 1.20 | v7.3.1 | v0.47.10-ics-lsm | v0.37.4 | Provider only (Cosmos Hub specific) | | [v4.0.0](https://github.com/cosmos/interchain-security/releases/tag/v4.0.0) | 1.21 | v7.3.1 | v0.47.7 | v0.37.4 | Provider on >= v4.0.0 backwards compatible with consumers >= v3.2.0 | +| [v4.1.1](https://github.com/cosmos/interchain-security/releases/tag/v4.1.1) | 1.21 | v7.4.0 | v0.47.10 | v0.37.4 | +| [v4.1.1-lsm](https://github.com/cosmos/interchain-security/releases/tag/v4.1.1-lsm) | 1.21 | v7.4.0 | v0.47.12-ics-lsm | v0.37.4 | Provider only (Cosmos Hub specific) | **Note:** For a list of major ICS features available in the currently active releases, see [FEATURES.md](./FEATURES.md). @@ -90,11 +93,12 @@ A MAJOR version of ICS will always be backwards compatible with the previous MAJ The following table indicates the compatibility of currently active releases: -| Consumer | Provider | `v3.2.0` | `v3.3.0` | `v3.3.3-lsm` | `v4.0.0` | -|----------|----------|----------|----------|--------------|----------| -| `v3.2.0` || ✅ | ✅ (1) | ✅ | ✅ | -| `v3.3.0` || ✅ (1) | ✅ | ✅ | ✅ | -| `v4.0.0` || ✅ (1)| ✅ (1)| ✅ (1) | ✅ | +| Consumer | Provider | `v3.2.0` | `v3.3.0` | `v3.3.3-lsm` | `v4.0.0` | `v4.1.1-lsm` | +|----------|----------|----------|----------|--------------|----------|--------------| +| `v3.2.0` || ✅ | ✅ (1) | ✅ | ✅ | ✅ | +| `v3.3.0` || ✅ (1) | ✅ | ✅ | ✅ | ✅ | +| `v4.0.0` || ✅ (1)| ✅ (1)| ✅ (1) | ✅ | ✅ | +| `v4.1.1` || ✅ (1)| ✅ (1)| ✅ (1) | ✅ | ✅ | #### Notes From 7bd9fa152524cc87806e9a0a46735a5a3b509faf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 10:30:39 +0200 Subject: [PATCH 003/102] build(deps): bump github.com/cosmos/cosmos-proto from 1.0.0-beta.4 to 1.0.0-beta.5 (#1802) build(deps): bump github.com/cosmos/cosmos-proto Bumps [github.com/cosmos/cosmos-proto](https://github.com/cosmos/cosmos-proto) from 1.0.0-beta.4 to 1.0.0-beta.5. - [Release notes](https://github.com/cosmos/cosmos-proto/releases) - [Commits](https://github.com/cosmos/cosmos-proto/compare/v1.0.0-beta.4...v1.0.0-beta.5) --- updated-dependencies: - dependency-name: github.com/cosmos/cosmos-proto dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 046e92170a..b5e53490df 100644 --- a/go.mod +++ b/go.mod @@ -62,7 +62,7 @@ require ( github.com/coinbase/rosetta-sdk-go/types v1.0.0 // indirect github.com/confio/ics23/go v0.9.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect - github.com/cosmos/cosmos-proto v1.0.0-beta.4 + github.com/cosmos/cosmos-proto v1.0.0-beta.5 github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/iavl v0.20.1 // indirect diff --git a/go.sum b/go.sum index f7c6506cda..d67b8a3f88 100644 --- a/go.sum +++ b/go.sum @@ -340,8 +340,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV 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-proto v1.0.0-beta.4 h1:aEL7tU/rLOmxZQ9z4i7mzxcLbSCY48OdY7lIWTLG7oU= -github.com/cosmos/cosmos-proto v1.0.0-beta.4/go.mod h1:oeB+FyVzG3XrQJbJng0EnV8Vljfk9XvTIpGILNU/9Co= +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.47.10 h1:Wxf5yEN3jZbG4fftxAMKB6rpd8ME0mxuCVihpz65dt0= github.com/cosmos/cosmos-sdk v0.47.10/go.mod h1:UWpgWkhcsBIATS68uUC0del7IiBN4hPv/vqg8Zz23uw= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= From f5f626ce6bcb61f00fd3e76b12f84122d1f300ec Mon Sep 17 00:00:00 2001 From: MSalopek Date: Tue, 30 Apr 2024 13:13:24 +0200 Subject: [PATCH 004/102] docs: add docs versioning and legacy page (pre v4.0.0) (#1833) * Updated build to allow for versioned docs * wip: add steps to build legacy docs * docs: add build legacy website * docs: add v4; rename to legacy * docs: add docs versioning for docusaurus v3.x * docs: add docs version sync and deploy scripts * update makefile * docs: rm deprecated build script * docs: fix banner in v4.1.0 * docs: update build script & config * update .gitignore * docs: update build script & config * docs: update Readme * build: update docs build script * build: update docs README.md * address review comments * address review comments -- update readme --------- Co-authored-by: Milan Mulji <98309852+mmulji-ic@users.noreply.github.com> --- .github/workflows/deploy-docs.yml | 4 ++- .gitignore | 4 +++ Makefile | 7 ++-- docs/README.md | 54 ++++++++++++++++++++++++------- docs/build.sh | 10 ------ docs/build_deploy.sh | 10 ++++++ docs/build_local.sh | 5 +++ docs/docusaurus.config.js | 26 +++++++++++++-- docs/sync_versions.sh | 30 +++++++++++++++++ docs/versions.json | 5 +++ 10 files changed, 127 insertions(+), 28 deletions(-) delete mode 100755 docs/build.sh create mode 100755 docs/build_deploy.sh create mode 100755 docs/build_local.sh create mode 100755 docs/sync_versions.sh create mode 100644 docs/versions.json diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 8e36cd6cda..f95a6c2b0e 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -37,7 +37,9 @@ jobs: - name: Build 🔧 run: | npm install -g npm@10.2.4 - make build-docs + cd docs + ./sync_versions.sh + ./build_deploy.sh - name: Deploy 🚀 uses: JamesIves/github-pages-deploy-action@v4.5.0 diff --git a/.gitignore b/.gitignore index e83a69a504..96e3251977 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ vendor/ build/ .vscode .idea + +# docusaurus versioned docs created during build +docs/versioned_docs +docs/versioned_sidebars diff --git a/Makefile b/Makefile index 26f2fe7f1d..15fd06355d 100644 --- a/Makefile +++ b/Makefile @@ -246,10 +246,11 @@ proto-update-deps: ### Documentation ### ############################################################################### -build-docs: - @cd docs && ./build.sh +build-docs-deploy: + @cd docs && ./sync_versions.sh && ./build_deploy.sh -.PHONY: build-docs +build-docs-local: + @cd docs && ./build_local.sh ############################################################################### ### Test Traces ### diff --git a/docs/README.md b/docs/README.md index aaba2fa1e1..d7be3a22ab 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,17 +1,17 @@ # Website -This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. +This website is built using [Docusaurus 3](https://docusaurus.io/), a modern static website generator. ### Installation ``` -$ yarn +$ npm install ``` ### Local Development ``` -$ yarn start +$ npm run start ``` This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. @@ -19,23 +19,55 @@ This command starts a local development server and opens up a browser window. Mo ### Build ``` -$ yarn build +$ npm run build ``` This command generates static content into the `build` directory and can be served using any static contents hosting service. -### Deployment -Using SSH: +# Adding versions +To add/remove versions from the page you can modify `versions.json`. + +At the time of writing it looked like this: +```json +[ + "v4.0.0", + "v4.1.0", + "v5.0.0-rc0" +] ``` -$ USE_SSH=true yarn deploy -``` -Not using SSH: +You can remove any version that you no longer need and the build process will remove it from the final page. + + +# Accessing versioned docs locally +```shell +# from interchain-security/docs run: +./sync_versions.sh ``` -$ GIT_USER= yarn deploy + +The script above will create `versioned_docs` and `versioned_sidebars` directories inside `interchain-security/docs`. + +To view the docs run: + +```shell +npm run start ``` -If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. +Remember to check back out to your working branch. Running `./sync_versions.sh` will leave you in a detached head state. +(simply run `git checkout ) + +## Note: +The script will exit if you have uncommitted changes. +The script switches branches while building the versioned docs - **please note that this could overwrite your local changes**. + + +# Legacy documentation + +`legacy-docs-page` [branch](https://github.com/cosmos/interchain-security/tree/legacy-docs-page) contains documentation for versions `<= v4.0.0`. These versions were built using docusaurus `v2.4.0` which is not compatible with docusaurus `v3.x` used at the time of writing. It was not feasible to port the legacy docs from `v2.4.0` because `v3.x` is not compatible with it and it required changing all release branches. + +The `legacy` directory on `legacy-docs-page` was created manually, by modifying `docusaurus.config.js` and `versions.json` on `https://github.com/cosmos/interchain-security/releases/v3.3.1-lsm` and generating the static pages manually using `npm run build`. + +The `legacy` directory gets included into the rest of the documentation using a simple `cp` command during the deploy process using the [build_deploy.sh](./build_deploy.sh) script. It is **not** included during local builds. diff --git a/docs/build.sh b/docs/build.sh deleted file mode 100755 index b39a0539a6..0000000000 --- a/docs/build.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -# This builds docs using docusaurus. -COMMIT=$(git rev-parse HEAD) -echo "building docusaurus main docs" -(git clean -fdx && git reset --hard && git checkout $COMMIT) -npm ci && npm run build -mv build ~/output -echo "done building docusaurus main docs" -# echo $DOCS_DOMAIN > ~/output/CNAME diff --git a/docs/build_deploy.sh b/docs/build_deploy.sh new file mode 100755 index 0000000000..9cc8fcf161 --- /dev/null +++ b/docs/build_deploy.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +echo "building docusaurus main docs" +npm ci && npm run build +# copy legacy docs to build folder +git fetch origin legacy-docs-page +git checkout origin/legacy-docs-page -- legacy +cp -r ./legacy ./build/ +mv build ~/output +echo "done building docusaurus main docs" diff --git a/docs/build_local.sh b/docs/build_local.sh new file mode 100755 index 0000000000..b93f147b0a --- /dev/null +++ b/docs/build_local.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +echo "building docusaurus from local branch" +npm ci && npm run build +echo "done building docusaurus from local docs" diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index e9c2b1e75c..5c85a14825 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -39,10 +39,23 @@ const config = { docs: { sidebarPath: require.resolve("./sidebars.js"), routeBasePath: "/", + lastVersion: "v4.1.0", versions: { current: { path: "/", - // banner: "current", + label: "main", + banner: "unreleased", + }, + "v4.0.0": { + path: "/v4.0.0/", + banner: "none", + }, + "v4.1.0": { + path: "/v4.1.0/", + banner: "none", + }, + "v5.0.0-rc0": { + banner: "unreleased", }, }, remarkPlugins: [remarkMath], @@ -91,7 +104,14 @@ const config = { { type: "docsVersionDropdown", position: "left", - dropdownActiveClassDisabled: true, + dropdownActiveClassDisabled: false, + dropdownItemsAfter: [ + { + href: "https://cosmos.github.io/interchain-security/legacy", + label: "<= v3.x", + target: "_blank", + }, + ], }, ], }, @@ -211,7 +231,7 @@ const config = { toExtensions: ["html"], redirects: [ { - from: ["/main", "/master"], + from: ["/main"], to: "/", }, ], diff --git a/docs/sync_versions.sh b/docs/sync_versions.sh new file mode 100755 index 0000000000..2aae4335d8 --- /dev/null +++ b/docs/sync_versions.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# this script builds the docs for all versions in versions.json +# all the versions are included in the docs webpage + +# exit if there are uncommited changes +if git diff-index --quiet HEAD --; then + # initial branch + COMMIT=$(git rev-parse HEAD) + for version in $(jq -r .[] versions.json); do + echo "Building docusaurus $version docs ..." + git checkout $version + npm cache clean --force && npm install && npm run docusaurus docs:version $version + + # versions.json / package-lock.json, get mangled by Docusarus causing problems + rm versions.json package-lock.json + done + + # return to initial branch but keep the files created by Docusarus in the loop above + (git reset --hard && git checkout $COMMIT) + + # copy figures so they are available to all versioned docs + cp -R figures ./versioned_docs/ +else + + echo "Please commit all changes before running this script" + exit 1 +fi + + diff --git a/docs/versions.json b/docs/versions.json new file mode 100644 index 0000000000..e0778f2e0e --- /dev/null +++ b/docs/versions.json @@ -0,0 +1,5 @@ +[ + "v4.0.0", + "v4.1.0", + "v5.0.0-rc0" +] \ No newline at end of file From 4550c338e745b3b60527a25711bbf59edc1533bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Apr 2024 14:18:41 +0200 Subject: [PATCH 005/102] build(deps): bump JamesIves/github-pages-deploy-action from 4.5.0 to 4.6.0 (#1804) build(deps): bump JamesIves/github-pages-deploy-action Bumps [JamesIves/github-pages-deploy-action](https://github.com/jamesives/github-pages-deploy-action) from 4.5.0 to 4.6.0. - [Release notes](https://github.com/jamesives/github-pages-deploy-action/releases) - [Commits](https://github.com/jamesives/github-pages-deploy-action/compare/v4.5.0...v4.6.0) --- updated-dependencies: - dependency-name: JamesIves/github-pages-deploy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/deploy-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index f95a6c2b0e..b8ff6c17c8 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -42,7 +42,7 @@ jobs: ./build_deploy.sh - name: Deploy 🚀 - uses: JamesIves/github-pages-deploy-action@v4.5.0 + uses: JamesIves/github-pages-deploy-action@v4.6.0 with: branch: gh-pages folder: ~/output From e5bd937f0e512d8a39399b43db7078bbe45bcf37 Mon Sep 17 00:00:00 2001 From: MSalopek Date: Tue, 30 Apr 2024 14:19:24 +0200 Subject: [PATCH 006/102] chore: add release/v5.x bots targets (#1829) --- .github/dependabot.yml | 12 +++++++++++- .mergify.yml | 10 +++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a5e6e0588b..382d63af3f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -66,4 +66,14 @@ updates: # Only allow automated security-related dependency updates on release branches. open-pull-requests-limit: 0 labels: - - dependencies \ No newline at end of file + - dependencies + + - package-ecosystem: gomod + directory: "/" + schedule: + interval: daily + target-branch: "release/v5.x" + # Only allow automated security-related dependency updates on release branches. + open-pull-requests-limit: 0 + labels: + - dependencies diff --git a/.mergify.yml b/.mergify.yml index bdad9f819e..8679f042eb 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -49,4 +49,12 @@ pull_request_rules: actions: backport: branches: - - release/v4.1.x-lsm \ No newline at end of file + - release/v4.1.x-lsm + - name: Backport patches to the release/v5.x branch + conditions: + - base=main + - label=A:backport/v5.x + actions: + backport: + branches: + - release/v5.x From 08c037657eef17a7bc4fed9fe00a3ce4f6393083 Mon Sep 17 00:00:00 2001 From: Cosmos SDK <113218068+github-prbot@users.noreply.github.com> Date: Tue, 30 Apr 2024 14:36:35 +0200 Subject: [PATCH 007/102] chore: fix spelling errors (#1835) chore: spelling errors fixes Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com> --- docs/sync_versions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sync_versions.sh b/docs/sync_versions.sh index 2aae4335d8..977a69700c 100755 --- a/docs/sync_versions.sh +++ b/docs/sync_versions.sh @@ -3,7 +3,7 @@ # this script builds the docs for all versions in versions.json # all the versions are included in the docs webpage -# exit if there are uncommited changes +# exit if there are uncommitted changes if git diff-index --quiet HEAD --; then # initial branch COMMIT=$(git rev-parse HEAD) From d282e079062eb393bad090984b588a2787864158 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:15:38 +0200 Subject: [PATCH 008/102] build(deps): bump github.com/hashicorp/go-getter from 1.7.1 to 1.7.4 (#1826) Bumps [github.com/hashicorp/go-getter](https://github.com/hashicorp/go-getter) from 1.7.1 to 1.7.4. - [Release notes](https://github.com/hashicorp/go-getter/releases) - [Changelog](https://github.com/hashicorp/go-getter/blob/main/.goreleaser.yml) - [Commits](https://github.com/hashicorp/go-getter/compare/v1.7.1...v1.7.4) --- updated-dependencies: - dependency-name: github.com/hashicorp/go-getter dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b5e53490df..db8b94586c 100644 --- a/go.mod +++ b/go.mod @@ -102,7 +102,7 @@ require ( 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.1 // indirect + github.com/hashicorp/go-getter v1.7.4 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-uuid v1.0.2 // indirect diff --git a/go.sum b/go.sum index d67b8a3f88..ba33844aa1 100644 --- a/go.sum +++ b/go.sum @@ -645,8 +645,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng 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.1 h1:SWiSWN/42qdpR0MdhaOc/bLR48PLuP1ZQtYLRlM69uY= -github.com/hashicorp/go-getter v1.7.1/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +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-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= From 4975d6b6492ecdd8bbc206caede16ae1d26eeb66 Mon Sep 17 00:00:00 2001 From: MSalopek Date: Tue, 30 Apr 2024 17:15:40 +0200 Subject: [PATCH 009/102] docs: update docs deployment (#1841) --- docs/README.md | 6 +++--- docs/build_deploy.sh | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/README.md b/docs/README.md index d7be3a22ab..4c4146ca4b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -66,8 +66,8 @@ The script switches branches while building the versioned docs - **please note t # Legacy documentation -`legacy-docs-page` [branch](https://github.com/cosmos/interchain-security/tree/legacy-docs-page) contains documentation for versions `<= v4.0.0`. These versions were built using docusaurus `v2.4.0` which is not compatible with docusaurus `v3.x` used at the time of writing. It was not feasible to port the legacy docs from `v2.4.0` because `v3.x` is not compatible with it and it required changing all release branches. +`legacy-docs-page` [branch](https://github.com/cosmos/interchain-security/tree/legacy-docs-page) contains documentation for versions `<= v4.0.0`. These versions were built using docusaurus `v2.4.0` which is not compatible with docusaurus `v3.x` used at the time of writing. It was not feasible to port the legacy docs from `v2.4.0` because `v3.x` is not compatible with it and it required changing all release branches and cutting patch releases. -The `legacy` directory on `legacy-docs-page` was created manually, by modifying `docusaurus.config.js` and `versions.json` on `https://github.com/cosmos/interchain-security/releases/v3.3.1-lsm` and generating the static pages manually using `npm run build`. +The `./docs/legacy` directory on `legacy-docs-page` was created manually, by modifying `docusaurus.config.js` and `versions.json` on `https://github.com/cosmos/interchain-security/releases/v3.3.1-lsm` and generating the static pages manually using `npm run build`. -The `legacy` directory gets included into the rest of the documentation using a simple `cp` command during the deploy process using the [build_deploy.sh](./build_deploy.sh) script. It is **not** included during local builds. +The `./docs/legacy` directory gets included into the rest of the documentation using a simple `cp` command during the deploy process using the [build_deploy.sh](./build_deploy.sh) script. It is **not** included during local builds. diff --git a/docs/build_deploy.sh b/docs/build_deploy.sh index 9cc8fcf161..ba262e43b2 100755 --- a/docs/build_deploy.sh +++ b/docs/build_deploy.sh @@ -1,9 +1,12 @@ #!/bin/sh +# build versioned docs prepared by sync_versions.sh echo "building docusaurus main docs" npm ci && npm run build -# copy legacy docs to build folder -git fetch origin legacy-docs-page + +# copy "legacy" docs directory into the final build directory +# the directory is in "docs/legacy" of the source branch (legacy-docs-page) +# the build environment must be in "./docs" for this to work as expected git checkout origin/legacy-docs-page -- legacy cp -r ./legacy ./build/ mv build ~/output From 2ace39c7f7206a6c15822bb6a5a278442f5b4745 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 16:07:13 +0200 Subject: [PATCH 010/102] build(deps): bump amannn/action-semantic-pull-request from 5.4.0 to 5.5.2 (#1831) build(deps): bump amannn/action-semantic-pull-request Bumps [amannn/action-semantic-pull-request](https://github.com/amannn/action-semantic-pull-request) from 5.4.0 to 5.5.2. - [Release notes](https://github.com/amannn/action-semantic-pull-request/releases) - [Changelog](https://github.com/amannn/action-semantic-pull-request/blob/main/CHANGELOG.md) - [Commits](https://github.com/amannn/action-semantic-pull-request/compare/v5.4.0...v5.5.2) --- updated-dependencies: - dependency-name: amannn/action-semantic-pull-request dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lint-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-pr.yml b/.github/workflows/lint-pr.yml index 18027164fc..7c573d1647 100644 --- a/.github/workflows/lint-pr.yml +++ b/.github/workflows/lint-pr.yml @@ -17,7 +17,7 @@ jobs: statuses: write # for amannn/action-semantic-pull-request to mark status of analyzed PR runs-on: ubuntu-latest steps: - - uses: amannn/action-semantic-pull-request@v5.4.0 + - uses: amannn/action-semantic-pull-request@v5.5.2 id: lint_pr_title env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From ab71eae431097fe1247e068278ad075f4bd3721b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 16:07:37 +0200 Subject: [PATCH 011/102] build(deps): bump bufbuild/buf-setup-action from 1.30.1 to 1.31.0 (#1832) Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.30.1 to 1.31.0. - [Release notes](https://github.com/bufbuild/buf-setup-action/releases) - [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.30.1...v1.31.0) --- updated-dependencies: - dependency-name: bufbuild/buf-setup-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/proto-registry.yml | 2 +- .github/workflows/proto.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/proto-registry.yml b/.github/workflows/proto-registry.yml index 2f1385be33..9938e96d22 100644 --- a/.github/workflows/proto-registry.yml +++ b/.github/workflows/proto-registry.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: bufbuild/buf-setup-action@v1.30.1 + - uses: bufbuild/buf-setup-action@v1.31.0 - uses: bufbuild/buf-push-action@v1 with: input: "proto" diff --git a/.github/workflows/proto.yml b/.github/workflows/proto.yml index 3c9d1b05b0..5e218cbb84 100644 --- a/.github/workflows/proto.yml +++ b/.github/workflows/proto.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: bufbuild/buf-setup-action@v1.30.1 + - uses: bufbuild/buf-setup-action@v1.31.0 - uses: bufbuild/buf-breaking-action@v1 with: input: "proto" From 8c4d87ff9b829eb7019a03ce77810777f6927a22 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Wed, 1 May 2024 16:59:05 +0200 Subject: [PATCH 012/102] feat!: Introduce Partial Set Security (#1809) * cleanup ./changelog entries * docs: changelog and release notes for v4.0.0 (#1564) * add v4.0.0 section to changelog * add release notes * fix!: Validation of SlashAcks fails due to marshaling to Bech32 (backport #1570) (#1577) fix!: Validation of SlashAcks fails due to marshaling to Bech32 (#1570) * add different Bech32Prefix for consumer and provider * separate app encoding and params * remove ConsumerValPubKey from ValidatorConfig * update addresses in tests * make SlashAcks consistent across chains * add comments for clarity * Regenerate traces * Fix argument order * set bech32prefix for provider to cosmos * add changelog entries * add consumer-double-downtime e2e test * update nightly-e2e workflow * fix typo * add consumer-double-downtime to testConfigs * remove changes on provider * skip invalid SlashAcks * seal the config * clear the outstanding downtime flag for new vals * add info on upgrading to v4.0.0 * fix upgrade handler * fix changeover e2e test * Update tests/e2e/config.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update tests/e2e/config.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * add AccountPrefix to ChainConfig * fix docstrings * update AccountAddressPrefix in app.go * fix consumer-misb e2e test --------- Co-authored-by: Philip Offtermatt Co-authored-by: Simon Noetzlin Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> (cherry picked from commit 86046926502f7b0ba795bebcdd1fdc97ac776573) Co-authored-by: Marius Poke * docs: update changelog for v4.0.0 (#1578) update changelog * docs: prepare for v4.0.0 (#1581) * unclog build * update release notes * update release date * feat!: enable Opt In and Top N chains through gov proposals (#1615) * init commit * added test * fixed tests * added changelog entry and comment * Update x/ccv/provider/keeper/proposal_test.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update .changelog/unreleased/features/1587-enable-opt-in-chains-through-gov-proposals.md Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update proto/interchain_security/ccv/provider/v1/provider.proto Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update proto/interchain_security/ccv/provider/v1/provider.proto Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update proto/interchain_security/ccv/provider/v1/provider.proto Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update proto/interchain_security/ccv/provider/v1/provider.proto Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update x/ccv/provider/keeper/keeper.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * changed to tabular test --------- Co-authored-by: insumity Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * feat!: introduce MsgOptIn and MsgOptOut (#1620) * init commit * cleaning up * changed cons to val address * Revert "changed cons to val address" This reverts commit a32e8829fee3cbbe50e363a0aa91ad62117a8a1d. * Update x/ccv/provider/keeper/keeper.go Co-authored-by: Simon Noetzlin * took into account comments * added key assignment * add contraint such that opt out only works if the chain is running --------- Co-authored-by: insumity Co-authored-by: Simon Noetzlin * test: MBT: Add partial set security to model (feature branch version) (#1627) * Port changes from branch to main * Add model analysis changes to Makefile * test: Ports key assignment to the driver on the PSS feature branch (#1628) * Port key assignment to MBT driver * Add comment and make var names clearer * feat!: automatically opt in validators that vote Yes on consumer addition proposals (#1629) * init commit * changed providerKeeper.GetProposedConsumerChain to return a bool * add logging mesages * one more log message * fix comment * added one more test case of NO vote and made tabular test * test: Add driver for PSS (#1636) * Port key assignment to MBT driver * Add PSS trace generation * Add PSS trace gen to longer trace gen * Start handling top N parameter for new consumers * Finish merge * Add handling for optin/optout steps * Remove expected error from OptIn, which should not error * set top N parameter during path setup * Add comment to setup.go * feat!: add PSS reward distribution spike (#1632) * PSS reward distribution * "add optin mapping to test" * Update app/provider/app.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * docs * add TODO * fix Dos vector in IBCMiddlewarea * add reformat * fix DOS issue and make integration tests pass * doc * add integration test * doc * Compute total vp per consumer * add comments * remove opt-in comments and add TODOs * format * Update x/ccv/provider/keeper/distribution.go Co-authored-by: insumity * add UT + doc * Update tests/integration/distribution.go Co-authored-by: insumity * Update tests/integration/distribution.go Co-authored-by: insumity * nits * Update x/ccv/provider/ibc_middleware.go Co-authored-by: Marius Poke * add panics in IBC Middleware ICS4wrapper funcs * address comments --------- Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Co-authored-by: insumity Co-authored-by: Marius Poke * feat! use protos to serialize opted-in validators (#1659) move OptedInValidators to proto Co-authored-by: insumity * feat!: PSS enable per-consumer chain commission (#1657) * add draft commission * implement consumer commission draft * formatting * add msg handling * improve UT * nits * Update x/ccv/provider/keeper/keeper.go Co-authored-by: insumity * Update proto/interchain_security/ccv/provider/v1/tx.proto Co-authored-by: Marius Poke * optimize keys * Update x/ccv/provider/keeper/keeper.go Co-authored-by: insumity * address comments * address comments * remove unnecessary check * Revert "remove unnecessary check" This reverts commit 2951e9bace04f6436d6ad1e4a11efcedd0be8cb1. * fix minor bug in StopConsumerChain --------- Co-authored-by: insumity Co-authored-by: Marius Poke * test: update integration test suite for PSS (#1687) * draft multi consumer transfer setup and test * format multi consumer distribution test * update test for democ consumer chains * nits * nit * docs: changelog and release notes for v4.0.0 (#1564) * add v4.0.0 section to changelog * add release notes * fix!: Validation of SlashAcks fails due to marshaling to Bech32 (backport #1570) (#1577) fix!: Validation of SlashAcks fails due to marshaling to Bech32 (#1570) * add different Bech32Prefix for consumer and provider * separate app encoding and params * remove ConsumerValPubKey from ValidatorConfig * update addresses in tests * make SlashAcks consistent across chains * add comments for clarity * Regenerate traces * Fix argument order * set bech32prefix for provider to cosmos * add changelog entries * add consumer-double-downtime e2e test * update nightly-e2e workflow * fix typo * add consumer-double-downtime to testConfigs * remove changes on provider * skip invalid SlashAcks * seal the config * clear the outstanding downtime flag for new vals * add info on upgrading to v4.0.0 * fix upgrade handler * fix changeover e2e test * Update tests/e2e/config.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update tests/e2e/config.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * add AccountPrefix to ChainConfig * fix docstrings * update AccountAddressPrefix in app.go * fix consumer-misb e2e test --------- Co-authored-by: Philip Offtermatt Co-authored-by: Simon Noetzlin Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> (cherry picked from commit 86046926502f7b0ba795bebcdd1fdc97ac776573) Co-authored-by: Marius Poke * docs: update changelog for v4.0.0 (#1578) update changelog * feat!: enable Opt In and Top N chains through gov proposals (#1615) * init commit * added test * fixed tests * added changelog entry and comment * Update x/ccv/provider/keeper/proposal_test.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update .changelog/unreleased/features/1587-enable-opt-in-chains-through-gov-proposals.md Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update proto/interchain_security/ccv/provider/v1/provider.proto Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update proto/interchain_security/ccv/provider/v1/provider.proto Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update proto/interchain_security/ccv/provider/v1/provider.proto Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update proto/interchain_security/ccv/provider/v1/provider.proto Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update x/ccv/provider/keeper/keeper.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * changed to tabular test --------- Co-authored-by: insumity Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * feat!: introduce MsgOptIn and MsgOptOut (#1620) * init commit * cleaning up * changed cons to val address * Revert "changed cons to val address" This reverts commit a32e8829fee3cbbe50e363a0aa91ad62117a8a1d. * Update x/ccv/provider/keeper/keeper.go Co-authored-by: Simon Noetzlin * took into account comments * added key assignment * add contraint such that opt out only works if the chain is running --------- Co-authored-by: insumity Co-authored-by: Simon Noetzlin * test: MBT: Add partial set security to model (feature branch version) (#1627) * Port changes from branch to main * Add model analysis changes to Makefile * feat!: add PSS reward distribution spike (#1632) * PSS reward distribution * "add optin mapping to test" * Update app/provider/app.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * docs * add TODO * fix Dos vector in IBCMiddlewarea * add reformat * fix DOS issue and make integration tests pass * doc * add integration test * doc * Compute total vp per consumer * add comments * remove opt-in comments and add TODOs * format * Update x/ccv/provider/keeper/distribution.go Co-authored-by: insumity * add UT + doc * Update tests/integration/distribution.go Co-authored-by: insumity * Update tests/integration/distribution.go Co-authored-by: insumity * nits * Update x/ccv/provider/ibc_middleware.go Co-authored-by: Marius Poke * add panics in IBC Middleware ICS4wrapper funcs * address comments --------- Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Co-authored-by: insumity Co-authored-by: Marius Poke * feat!: PSS enable per-consumer chain commission (#1657) * add draft commission * implement consumer commission draft * formatting * add msg handling * improve UT * nits * Update x/ccv/provider/keeper/keeper.go Co-authored-by: insumity * Update proto/interchain_security/ccv/provider/v1/tx.proto Co-authored-by: Marius Poke * optimize keys * Update x/ccv/provider/keeper/keeper.go Co-authored-by: insumity * address comments * address comments * remove unnecessary check * Revert "remove unnecessary check" This reverts commit 2951e9bace04f6436d6ad1e4a11efcedd0be8cb1. * fix minor bug in StopConsumerChain --------- Co-authored-by: insumity Co-authored-by: Marius Poke * fix nits in MBT model after merging #1676 from main * Fix merging ccv model * Remove conflict markers * Remove more conflict markers * EndProviderEpoch takes ConsumerAdditionMsg * Fix using consumer addition msgs instead of chain names in boundeddrift.qnt * lint * chore: rebase PSS branch with main (#1689) * Update tests/mbt/driver/mbt_test.go * nits * revert unwanted line deletion from linter --------- Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * feat!: complete the PSS reward distribution (#1709) * update compute consumer total power for reward distribution * update distribution logic to work with epochcs * Adapt reward distribution mem test to epochs * doc * nits * other nits * nits * Update tests/integration/distribution.go * feat!: Add slashing logic for PSS (#1710) * add check for consumer validators in downtime logic * fix UT * try to fix weird errors in gh worfklow * fix silly merge bug * nits * ci: do not scan the tests for security issues (#1717) init commit * feat!: compute partial sets (#1702) * init commit * nit change * cleaning up * clean up * fix distribution test * Update x/ccv/provider/keeper/hooks.go Co-authored-by: Simon Noetzlin * took into Simon's comments * took into rest of the comments * nit change * return an error if validator cannot opt out from a Top N chain * removed automatic opt-in for validators that vote Yes on proposals * tiny fix for E2E tests * nit change to remove unecessary else * fixed topN == 0 issue --------- Co-authored-by: Simon Noetzlin * feat!: update PSS cli (#1708) finalize PSS CLI cmds * Rename and add comission rate command to commands * feat!: only perform consumer additions for non-empty chains (#1730) * init commit * Update x/ccv/provider/keeper/proposal.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> --------- Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * feat: Add queries for PSS and consumer commission rate (#1733) * init commit * nit change * cleaning up * clean up * fix distribution test * Update x/ccv/provider/keeper/hooks.go Co-authored-by: Simon Noetzlin * took into Simon's comments * took into rest of the comments * nit change * return an error if validator cannot opt out from a Top N chain * removed automatic opt-in for validators that vote Yes on proposals * tiny fix for E2E tests * nit change to remove unecessary else * update consumer chains query to return topN * update query consu chains proto * add consumer chains per validator query * Add PSS command to provider's cli * nits * add consumer commission rate query * nits * big renaming * fix doc * nits * nits * docs * Update proto/interchain_security/ccv/provider/v1/query.proto Co-authored-by: insumity * nit * add OptedIn in QueryConsumerChainsValidatorHasToValidate * remove OptIn field in consumer chains query response * include validators that opt-in during the next epochs * update has-to-validate condition * fix tinny bug in the tests after merging feat/partial-security * update doc * update cli description * Update x/ccv/provider/keeper/grpc_query.go Co-authored-by: insumity * changes --------- Co-authored-by: insumity * fix!: Fix opt-in assignment (#1732) * Make the same validator assigning the same key a noop instead of an error * Adjust test * Update tests * Fix newline warning * Regenerate traces * Add key assignment change to changelog * Add info log for same key same validator assignments * Add changelog entry to api-breaking * Update x/ccv/provider/handler_test.go Co-authored-by: insumity * Add more comments to test and return right validator --------- Co-authored-by: insumity * fix silly bug in PSS opted-in val query * fix logging in ibc_module.go * test: add partial-set-security E2E tests (#1737) * init commit * fix traces * Add PSS to default tests * Update tests/e2e/steps_partial_set_security.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update tests/e2e/steps_partial_set_security.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> --------- Co-authored-by: Philip Offtermatt Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Nit changes based on Simons comments. To be pushed directly because E2E PR 1737 were the comments were written was accidentally merged. * fix!: return a SlashAck even if the validator is not a consumer validator (#1763) * init commit * fix test * test: Expand PSS e2e test to include slashing (#1752) * Expand test to include slashing * Add back existing steps * Add downtime to top N test * Fix nits * fix!: update unbonding pausing for PSS (#1728) * draft PSS unbonding fix * fix hook logic to retrieve validator address from ubd op * add unbonding pausing unit-test * remove panic in hook * Get back 3.2.0 and 3.3.0 changelog from main * Port epilogue from main * Fix proto conflict * generate proto files * Port RELEASE_NOTES * Fix merge for tests * Merge declaration and assignment * Clean up model files * Add pss tests to MBT readme * Restore MsgSubmitConsumerDoubleVoting handler * Remove local driver files * Remove Quint guidelines * Add optin/optout to MBT readme * Fix types in model * Fix model * Add migration * Ensure SlashAcks are sent even when the valset does not change * adding changelog entry * Empty DowntimeSlachAcks on EndBlock * Remove logs * Change condition for sending slash acks * Revert model changes * Start fixing PSS issues in model * Add expected errors to opt out action * Revert PSS quint model changes * Add parameter to ComputeNextEpochConsumerValSet * Set top N param in setup * Fix: do not try key assignment if there is no nonjailed validator * Do not assign keys for jailed validators * Only jail validators with non-zero-power * Add unit test * Add unit test for unset case * Panic on not being able to unmarshal * Move packet handling into ack.Success block * Format * Remove unnecessary comment * Add parens for clarity * Format and fix typo * Move OptIn/OptOut events to provider events * Remove unused function * Improve comments for keys * Improve comments for key getter functions * Remove order change for existing keys * Re-add nolint instruction * nit comment fix * Move ConsumerAllocationTests to correct folder * nit comment fix * fix!: handle consumer commission marshalling errors gracefully (#1836) * handle consumer commission setter/getter gracefully to avoid BeginBlock panic + add msg in codec * fix consumer commission query rest path * fix: update queries REST path for PSS (#1839) update queries rest path * Clarify that GetProposedConsumerChain is test-only * fix: Fix has-to-validate query (#1823) * Fix has-to-validate query * Flip comparison sign for checking minPower * Regenerate traces * Remove unnecessary print * Address comments * fix!: fix slashing in PSS (#1838) * drop slash packet for opted-out validators before updating slash meter * fix integration test * fix ut * update UT * Update x/ccv/provider/types/msg.go Co-authored-by: Marius Poke * Remove BlockValidatorUpdate from expected staking keeper * added an error response value to ComputeMinPowerToOptIn * delete additional state when we stop a chain * Assign keys and change voting power only for unjailed nodes with >0 power * fix: Validate consumer commission rate against minimal rate (#1834) * Validate consumer commission rate * Add test for commission rates * Remove static minimum commission rate validation from Set * feat!: introduce power shaping (#1830) * added power shaping * fixes * Add property based test for power cap * fixed tests & added algorithm's idea * nit changes * Update x/ccv/provider/keeper/proposal.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * remove empty-validator-set check * implicit memory aliasing issue fixed * added keeper tests * updated HasToValidate query * Update x/ccv/provider/keeper/keeper.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update x/ccv/provider/keeper/keeper.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * took into account comments * do not use cached ctx * Fix E2E test. A jailed validator does not have to validate. * fix merge issue and format --------- Co-authored-by: Philip Offtermatt Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update v4 to v5 in package version * Bump consensus version * Add migration in correct folder * Update version from v4 to v5 in migration --------- Co-authored-by: mpoke Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: insumity Co-authored-by: insumity Co-authored-by: Simon Noetzlin --- .changelog/unreleased/.gitkeep | 0 ...1732-assigning-already-assigned-key-fix.md | 2 + .changelog/unreleased/features/1809-pss.md | 3 + ...ble-opt-in-chains-through-gov-proposals.md | 2 + .../unreleased/state-breaking/1809-pss.md | 2 + ...ble-opt-in-chains-through-gov-proposals.md | 2 + ...1732-assigning-already-assigned-key-fix.md | 2 + .../ante/forbidden_proposals_ante_test.go | 4 +- app/consumer-democracy/ante_handler.go | 6 +- app/consumer-democracy/app.go | 16 +- .../proposals_whitelisting_test.go | 6 +- .../ante/disabled_modules_ante_test.go | 4 +- app/consumer/ante/msg_filter_ante_test.go | 4 +- app/consumer/ante_handler.go | 4 +- app/consumer/app.go | 10 +- app/consumer/genesis.go | 4 +- app/consumer/genesis_test.go | 6 +- app/provider/app.go | 26 +- app/sovereign/app.go | 5 +- cmd/interchain-security-cd/cmd/root.go | 4 +- cmd/interchain-security-cd/main.go | 6 +- cmd/interchain-security-cdd/cmd/root.go | 4 +- cmd/interchain-security-cdd/main.go | 6 +- cmd/interchain-security-pd/cmd/root.go | 4 +- cmd/interchain-security-pd/main.go | 6 +- cmd/interchain-security-sd/cmd/root.go | 4 +- cmd/interchain-security-sd/main.go | 6 +- go.mod | 2 +- .../ccv/consumer/v1/consumer.proto | 2 +- .../ccv/consumer/v1/genesis.proto | 2 +- .../ccv/consumer/v1/query.proto | 2 +- .../ccv/provider/v1/genesis.proto | 2 +- .../ccv/provider/v1/provider.proto | 35 +- .../ccv/provider/v1/query.proto | 69 +- .../ccv/provider/v1/tx.proto | 52 +- .../ccv/v1/shared_consumer.proto | 2 +- proto/interchain_security/ccv/v1/wire.proto | 2 +- scripts/protocgen.sh | 2 +- tests/e2e/actions.go | 124 +- tests/e2e/builder.go | 1 - tests/e2e/main.go | 14 +- tests/e2e/state.go | 32 + tests/e2e/steps_compatibility.go | 13 +- tests/e2e/steps_consumer_misbehaviour.go | 1 + tests/e2e/steps_partial_set_security.go | 1002 ++++++++++ tests/e2e/steps_sovereign_changeover.go | 1 + tests/e2e/steps_start_chains.go | 17 +- tests/e2e/test_driver.go | 4 + .../e2e/tracehandler_testdata/changeover.json | 20 +- .../consumer-double-sign.json | 43 +- .../consumer-misbehaviour.json | 16 +- .../e2e/tracehandler_testdata/democracy.json | 58 +- .../democracyRewardsSteps.json | 58 +- .../e2e/tracehandler_testdata/happyPath.json | 87 +- .../multipleConsumers.json | 157 +- .../e2e/tracehandler_testdata/shorthappy.json | 70 +- .../tracehandler_testdata/slashThrottle.json | 58 +- tests/integration/common.go | 8 +- tests/integration/democracy.go | 6 +- tests/integration/distribution.go | 712 ++++++- tests/integration/double_vote.go | 4 +- tests/integration/expired_client.go | 2 +- tests/integration/instance_test.go | 10 +- tests/integration/key_assignment.go | 61 +- tests/integration/misbehaviour.go | 4 +- tests/integration/normal_operations.go | 4 +- tests/integration/setup.go | 79 +- tests/integration/slashing.go | 15 +- tests/integration/soft_opt_out.go | 4 +- tests/integration/stop_consumer.go | 2 +- tests/integration/throttle.go | 8 +- tests/integration/throttle_retry.go | 2 +- tests/integration/unbonding.go | 4 +- tests/integration/valset_update.go | 2 +- tests/mbt/driver/core.go | 16 +- tests/mbt/driver/mbt_test.go | 6 +- tests/mbt/driver/setup.go | 17 +- tests/mbt/model/README.md | 20 +- tests/mbt/model/ccv.qnt | 10 +- tests/mbt/model/ccv_model.qnt | 16 +- tests/mbt/model/ccv_pss.qnt | 157 ++ tests/mbt/model/ccv_pss_model.qnt | 113 ++ tests/mbt/model/ccv_pss_test.qnt | 61 + tests/mbt/model/ccv_utils.qnt | 5 + testutil/crypto/crypto.go | 2 +- testutil/ibc_testing/generic_setup.go | 33 +- testutil/ibc_testing/specific_setup.go | 10 +- testutil/integration/debug_test.go | 30 +- testutil/integration/interfaces.go | 7 +- testutil/keeper/expectations.go | 4 +- testutil/keeper/mocks.go | 214 ++- testutil/keeper/unit_test_helpers.go | 20 +- x/ccv/consumer/client/cli/query.go | 2 +- x/ccv/consumer/ibc_module.go | 6 +- x/ccv/consumer/ibc_module_test.go | 8 +- x/ccv/consumer/keeper/changeover_test.go | 4 +- x/ccv/consumer/keeper/distribution.go | 4 +- x/ccv/consumer/keeper/distribution_test.go | 6 +- x/ccv/consumer/keeper/genesis.go | 4 +- x/ccv/consumer/keeper/genesis_test.go | 10 +- x/ccv/consumer/keeper/grpc_query.go | 4 +- x/ccv/consumer/keeper/hooks.go | 2 +- x/ccv/consumer/keeper/keeper.go | 4 +- x/ccv/consumer/keeper/keeper_test.go | 8 +- x/ccv/consumer/keeper/migrations.go | 2 +- x/ccv/consumer/keeper/params.go | 2 +- x/ccv/consumer/keeper/params_test.go | 4 +- x/ccv/consumer/keeper/provider_info.go | 4 +- x/ccv/consumer/keeper/relay.go | 4 +- x/ccv/consumer/keeper/relay_test.go | 10 +- x/ccv/consumer/keeper/soft_opt_out.go | 2 +- x/ccv/consumer/keeper/soft_opt_out_test.go | 6 +- x/ccv/consumer/keeper/throttle_retry.go | 2 +- x/ccv/consumer/keeper/throttle_retry_test.go | 6 +- x/ccv/consumer/keeper/validators.go | 2 +- x/ccv/consumer/keeper/validators_test.go | 8 +- x/ccv/consumer/migrations/v2/migration.go | 4 +- .../consumer/migrations/v2/migration_test.go | 8 +- x/ccv/consumer/module.go | 8 +- x/ccv/consumer/types/genesis.go | 2 +- x/ccv/consumer/types/genesis.pb.go | 2 +- x/ccv/consumer/types/genesis_test.go | 6 +- x/ccv/consumer/types/keys.go | 2 +- x/ccv/consumer/types/params_test.go | 2 +- x/ccv/consumer/types/query.pb.go | 2 +- x/ccv/democracy/distribution/module.go | 2 +- x/ccv/provider/client/cli/query.go | 125 +- x/ccv/provider/client/cli/tx.go | 134 +- x/ccv/provider/client/proposal_handler.go | 18 +- x/ccv/provider/handler.go | 13 +- x/ccv/provider/handler_test.go | 43 +- x/ccv/provider/ibc_middleware.go | 242 +++ x/ccv/provider/ibc_middleware_test.go | 77 + x/ccv/provider/ibc_module.go | 6 +- x/ccv/provider/ibc_module_test.go | 10 +- .../provider/keeper/consumer_equivocation.go | 4 +- .../keeper/consumer_equivocation_test.go | 6 +- x/ccv/provider/keeper/distribution.go | 268 ++- x/ccv/provider/keeper/distribution_test.go | 272 +++ x/ccv/provider/keeper/genesis.go | 4 +- x/ccv/provider/keeper/genesis_test.go | 10 +- x/ccv/provider/keeper/grpc_query.go | 110 +- x/ccv/provider/keeper/grpc_query_test.go | 8 +- x/ccv/provider/keeper/hooks.go | 63 +- x/ccv/provider/keeper/hooks_test.go | 12 +- x/ccv/provider/keeper/keeper.go | 409 +++- x/ccv/provider/keeper/keeper_test.go | 225 ++- x/ccv/provider/keeper/key_assignment.go | 74 +- x/ccv/provider/keeper/key_assignment_test.go | 15 +- x/ccv/provider/keeper/msg_server.go | 155 +- x/ccv/provider/keeper/params.go | 4 +- x/ccv/provider/keeper/params_test.go | 4 +- x/ccv/provider/keeper/partial_set_security.go | 295 +++ .../keeper/partial_set_security_test.go | 676 +++++++ x/ccv/provider/keeper/proposal.go | 59 +- x/ccv/provider/keeper/proposal_test.go | 106 +- x/ccv/provider/keeper/relay.go | 26 +- x/ccv/provider/keeper/relay_test.go | 248 ++- x/ccv/provider/keeper/throttle.go | 2 +- x/ccv/provider/keeper/throttle_legacy.go | 4 +- x/ccv/provider/keeper/throttle_test.go | 4 +- x/ccv/provider/keeper/validator_set_update.go | 121 +- .../keeper/validator_set_update_test.go | 228 ++- x/ccv/provider/migrations/migrator.go | 14 +- .../provider/migrations/v3/migration_test.go | 2 +- x/ccv/provider/migrations/v3/migrations.go | 2 +- .../provider/migrations/v4/migration_test.go | 4 +- x/ccv/provider/migrations/v4/migrations.go | 2 +- .../provider/migrations/v5/migration_test.go | 30 + x/ccv/provider/migrations/v5/migrations.go | 24 + x/ccv/provider/module.go | 14 +- x/ccv/provider/module_test.go | 8 +- x/ccv/provider/proposal_handler.go | 4 +- x/ccv/provider/proposal_handler_test.go | 11 +- x/ccv/provider/types/codec.go | 12 + x/ccv/provider/types/consumer.go | 2 +- x/ccv/provider/types/errors.go | 4 +- x/ccv/provider/types/events.go | 5 + x/ccv/provider/types/genesis.go | 2 +- x/ccv/provider/types/genesis.pb.go | 2 +- x/ccv/provider/types/genesis_test.go | 6 +- x/ccv/provider/types/key_assignment.go | 2 +- x/ccv/provider/types/keys.go | 83 +- x/ccv/provider/types/keys_test.go | 8 +- x/ccv/provider/types/msg.go | 168 +- x/ccv/provider/types/params.go | 2 +- x/ccv/provider/types/params_test.go | 2 +- x/ccv/provider/types/proposal.go | 22 +- x/ccv/provider/types/proposal_test.go | 124 +- x/ccv/provider/types/provider.pb.go | 620 +++++-- x/ccv/provider/types/query.pb.go | 1498 +++++++++++++-- x/ccv/provider/types/query.pb.gw.go | 325 ++++ x/ccv/provider/types/tx.pb.go | 1650 ++++++++++++++--- x/ccv/types/expected_keepers.go | 9 + x/ccv/types/utils_test.go | 2 +- x/ccv/types/wire_test.go | 4 +- 196 files changed, 11433 insertions(+), 1230 deletions(-) create mode 100644 .changelog/unreleased/.gitkeep create mode 100644 .changelog/unreleased/api-breaking/provider/1732-assigning-already-assigned-key-fix.md create mode 100644 .changelog/unreleased/features/1809-pss.md create mode 100644 .changelog/unreleased/features/provider/1587-enable-opt-in-chains-through-gov-proposals.md create mode 100644 .changelog/unreleased/state-breaking/1809-pss.md create mode 100644 .changelog/unreleased/state-breaking/provider/1587-enable-opt-in-chains-through-gov-proposals.md create mode 100644 .changelog/unreleased/state-breaking/provider/1732-assigning-already-assigned-key-fix.md create mode 100644 tests/e2e/steps_partial_set_security.go create mode 100644 tests/mbt/model/ccv_pss.qnt create mode 100644 tests/mbt/model/ccv_pss_model.qnt create mode 100644 tests/mbt/model/ccv_pss_test.qnt create mode 100644 x/ccv/provider/ibc_middleware.go create mode 100644 x/ccv/provider/ibc_middleware_test.go create mode 100644 x/ccv/provider/keeper/distribution_test.go create mode 100644 x/ccv/provider/keeper/partial_set_security.go create mode 100644 x/ccv/provider/keeper/partial_set_security_test.go create mode 100644 x/ccv/provider/migrations/v5/migration_test.go create mode 100644 x/ccv/provider/migrations/v5/migrations.go diff --git a/.changelog/unreleased/.gitkeep b/.changelog/unreleased/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.changelog/unreleased/api-breaking/provider/1732-assigning-already-assigned-key-fix.md b/.changelog/unreleased/api-breaking/provider/1732-assigning-already-assigned-key-fix.md new file mode 100644 index 0000000000..667a481d3f --- /dev/null +++ b/.changelog/unreleased/api-breaking/provider/1732-assigning-already-assigned-key-fix.md @@ -0,0 +1,2 @@ +- Assigning a key that is already assigned by the same validator will now be a no-op instead of throwing an error. + ([\#1732](https://github.com/cosmos/interchain-security/pull/1732)) \ No newline at end of file diff --git a/.changelog/unreleased/features/1809-pss.md b/.changelog/unreleased/features/1809-pss.md new file mode 100644 index 0000000000..f7a235c5be --- /dev/null +++ b/.changelog/unreleased/features/1809-pss.md @@ -0,0 +1,3 @@ +- Adding the Partial Set Security (PSS) feature cf. [ADR 015](https://cosmos.github.io/interchain-security/adrs/adr-015-partial-set-security). + PSS enables consumer chains to join ICS as _Top N_ or _Opt In_ chains and enables validators to opt to validate the consumer chains they want. + ([\#1809](https://github.com/cosmos/interchain-security/pull/1809)) \ No newline at end of file diff --git a/.changelog/unreleased/features/provider/1587-enable-opt-in-chains-through-gov-proposals.md b/.changelog/unreleased/features/provider/1587-enable-opt-in-chains-through-gov-proposals.md new file mode 100644 index 0000000000..57f16adc9b --- /dev/null +++ b/.changelog/unreleased/features/provider/1587-enable-opt-in-chains-through-gov-proposals.md @@ -0,0 +1,2 @@ +- Enable Opt In and Top N chains through gov proposals. + ([\#1587](https://github.com/cosmos/interchain-security/pull/1587)) \ No newline at end of file diff --git a/.changelog/unreleased/state-breaking/1809-pss.md b/.changelog/unreleased/state-breaking/1809-pss.md new file mode 100644 index 0000000000..c0af9ae11e --- /dev/null +++ b/.changelog/unreleased/state-breaking/1809-pss.md @@ -0,0 +1,2 @@ +- Adding the Partial Set Security feature cf. [ADR 015](https://cosmos.github.io/interchain-security/adrs/adr-015-partial-set-security). + ([\#1809](https://github.com/cosmos/interchain-security/pull/1809)) \ No newline at end of file diff --git a/.changelog/unreleased/state-breaking/provider/1587-enable-opt-in-chains-through-gov-proposals.md b/.changelog/unreleased/state-breaking/provider/1587-enable-opt-in-chains-through-gov-proposals.md new file mode 100644 index 0000000000..57f16adc9b --- /dev/null +++ b/.changelog/unreleased/state-breaking/provider/1587-enable-opt-in-chains-through-gov-proposals.md @@ -0,0 +1,2 @@ +- Enable Opt In and Top N chains through gov proposals. + ([\#1587](https://github.com/cosmos/interchain-security/pull/1587)) \ No newline at end of file diff --git a/.changelog/unreleased/state-breaking/provider/1732-assigning-already-assigned-key-fix.md b/.changelog/unreleased/state-breaking/provider/1732-assigning-already-assigned-key-fix.md new file mode 100644 index 0000000000..667a481d3f --- /dev/null +++ b/.changelog/unreleased/state-breaking/provider/1732-assigning-already-assigned-key-fix.md @@ -0,0 +1,2 @@ +- Assigning a key that is already assigned by the same validator will now be a no-op instead of throwing an error. + ([\#1732](https://github.com/cosmos/interchain-security/pull/1732)) \ No newline at end of file diff --git a/app/consumer-democracy/ante/forbidden_proposals_ante_test.go b/app/consumer-democracy/ante/forbidden_proposals_ante_test.go index 8fd7fe3824..c4c7123290 100644 --- a/app/consumer-democracy/ante/forbidden_proposals_ante_test.go +++ b/app/consumer-democracy/ante/forbidden_proposals_ante_test.go @@ -14,8 +14,8 @@ import ( minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" "github.com/cosmos/cosmos-sdk/x/params/types/proposal" - app "github.com/cosmos/interchain-security/v4/app/consumer-democracy" - "github.com/cosmos/interchain-security/v4/app/consumer-democracy/ante" + app "github.com/cosmos/interchain-security/v5/app/consumer-democracy" + "github.com/cosmos/interchain-security/v5/app/consumer-democracy/ante" ) // in SDKv47 parameter updates full params object is required diff --git a/app/consumer-democracy/ante_handler.go b/app/consumer-democracy/ante_handler.go index 7e652ebb0c..b0f7432fb6 100644 --- a/app/consumer-democracy/ante_handler.go +++ b/app/consumer-democracy/ante_handler.go @@ -10,9 +10,9 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" - democracyante "github.com/cosmos/interchain-security/v4/app/consumer-democracy/ante" - consumerante "github.com/cosmos/interchain-security/v4/app/consumer/ante" - ibcconsumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + democracyante "github.com/cosmos/interchain-security/v5/app/consumer-democracy/ante" + consumerante "github.com/cosmos/interchain-security/v5/app/consumer/ante" + ibcconsumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" ) // HandlerOptions extend the SDK's AnteHandler options by requiring the IBC diff --git a/app/consumer-democracy/app.go b/app/consumer-democracy/app.go index e90d35760e..190e6680d8 100644 --- a/app/consumer-democracy/app.go +++ b/app/consumer-democracy/app.go @@ -103,14 +103,14 @@ import ( "github.com/cometbft/cometbft/libs/log" tmos "github.com/cometbft/cometbft/libs/os" - appencoding "github.com/cosmos/interchain-security/v4/app/encoding" - testutil "github.com/cosmos/interchain-security/v4/testutil/integration" - consumer "github.com/cosmos/interchain-security/v4/x/ccv/consumer" - consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccvdistr "github.com/cosmos/interchain-security/v4/x/ccv/democracy/distribution" - ccvgov "github.com/cosmos/interchain-security/v4/x/ccv/democracy/governance" - ccvstaking "github.com/cosmos/interchain-security/v4/x/ccv/democracy/staking" + appencoding "github.com/cosmos/interchain-security/v5/app/encoding" + testutil "github.com/cosmos/interchain-security/v5/testutil/integration" + consumer "github.com/cosmos/interchain-security/v5/x/ccv/consumer" + consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccvdistr "github.com/cosmos/interchain-security/v5/x/ccv/democracy/distribution" + ccvgov "github.com/cosmos/interchain-security/v5/x/ccv/democracy/governance" + ccvstaking "github.com/cosmos/interchain-security/v5/x/ccv/democracy/staking" ) const ( diff --git a/app/consumer-democracy/proposals_whitelisting_test.go b/app/consumer-democracy/proposals_whitelisting_test.go index 47dd42317a..c4209bf3b9 100644 --- a/app/consumer-democracy/proposals_whitelisting_test.go +++ b/app/consumer-democracy/proposals_whitelisting_test.go @@ -6,9 +6,9 @@ import ( ibctesting "github.com/cosmos/ibc-go/v7/testing" "github.com/stretchr/testify/require" - appConsumer "github.com/cosmos/interchain-security/v4/app/consumer-democracy" - icstestingutils "github.com/cosmos/interchain-security/v4/testutil/ibc_testing" - testutil "github.com/cosmos/interchain-security/v4/testutil/integration" + appConsumer "github.com/cosmos/interchain-security/v5/app/consumer-democracy" + icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" + testutil "github.com/cosmos/interchain-security/v5/testutil/integration" ) func TestDemocracyGovernanceWhitelistingKeys(t *testing.T) { diff --git a/app/consumer/ante/disabled_modules_ante_test.go b/app/consumer/ante/disabled_modules_ante_test.go index 7fa95f37c6..d06c3ad595 100644 --- a/app/consumer/ante/disabled_modules_ante_test.go +++ b/app/consumer/ante/disabled_modules_ante_test.go @@ -12,8 +12,8 @@ import ( evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" - "github.com/cosmos/interchain-security/v4/app/consumer/ante" - appencoding "github.com/cosmos/interchain-security/v4/app/encoding" + "github.com/cosmos/interchain-security/v5/app/consumer/ante" + appencoding "github.com/cosmos/interchain-security/v5/app/encoding" ) func TestDisabledModulesDecorator(t *testing.T) { diff --git a/app/consumer/ante/msg_filter_ante_test.go b/app/consumer/ante/msg_filter_ante_test.go index bfc1bb0a50..9dd5f47ef3 100644 --- a/app/consumer/ante/msg_filter_ante_test.go +++ b/app/consumer/ante/msg_filter_ante_test.go @@ -9,8 +9,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/cosmos/interchain-security/v4/app/consumer/ante" - appencoding "github.com/cosmos/interchain-security/v4/app/encoding" + "github.com/cosmos/interchain-security/v5/app/consumer/ante" + appencoding "github.com/cosmos/interchain-security/v5/app/encoding" ) type consumerKeeper struct { diff --git a/app/consumer/ante_handler.go b/app/consumer/ante_handler.go index fa28a52caf..f9cd986dad 100644 --- a/app/consumer/ante_handler.go +++ b/app/consumer/ante_handler.go @@ -10,8 +10,8 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" - consumerante "github.com/cosmos/interchain-security/v4/app/consumer/ante" - ibcconsumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + consumerante "github.com/cosmos/interchain-security/v5/app/consumer/ante" + ibcconsumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" ) // HandlerOptions extend the SDK's AnteHandler options by requiring the IBC diff --git a/app/consumer/app.go b/app/consumer/app.go index 9853145117..207939adcb 100644 --- a/app/consumer/app.go +++ b/app/consumer/app.go @@ -87,11 +87,11 @@ import ( "github.com/cometbft/cometbft/libs/log" tmos "github.com/cometbft/cometbft/libs/os" - appencoding "github.com/cosmos/interchain-security/v4/app/encoding" - testutil "github.com/cosmos/interchain-security/v4/testutil/integration" - ibcconsumer "github.com/cosmos/interchain-security/v4/x/ccv/consumer" - ibcconsumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" - ibcconsumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + appencoding "github.com/cosmos/interchain-security/v5/app/encoding" + testutil "github.com/cosmos/interchain-security/v5/testutil/integration" + ibcconsumer "github.com/cosmos/interchain-security/v5/x/ccv/consumer" + ibcconsumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" + ibcconsumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" ) const ( diff --git a/app/consumer/genesis.go b/app/consumer/genesis.go index f6c8e47905..da55a0334e 100644 --- a/app/consumer/genesis.go +++ b/app/consumer/genesis.go @@ -16,8 +16,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" - consumerTypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - "github.com/cosmos/interchain-security/v4/x/ccv/types" + consumerTypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // The genesis state of the blockchain is represented here as a map of raw json diff --git a/app/consumer/genesis_test.go b/app/consumer/genesis_test.go index 5827651959..2dd29e1b71 100644 --- a/app/consumer/genesis_test.go +++ b/app/consumer/genesis_test.go @@ -16,9 +16,9 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/x/auth/types" - app "github.com/cosmos/interchain-security/v4/app/consumer" - consumerTypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + app "github.com/cosmos/interchain-security/v5/app/consumer" + consumerTypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) const ( diff --git a/app/provider/app.go b/app/provider/app.go index 7527756d45..e5baa31270 100644 --- a/app/provider/app.go +++ b/app/provider/app.go @@ -100,12 +100,13 @@ import ( "github.com/cometbft/cometbft/libs/log" tmos "github.com/cometbft/cometbft/libs/os" - appencoding "github.com/cosmos/interchain-security/v4/app/encoding" - testutil "github.com/cosmos/interchain-security/v4/testutil/integration" - ibcprovider "github.com/cosmos/interchain-security/v4/x/ccv/provider" - ibcproviderclient "github.com/cosmos/interchain-security/v4/x/ccv/provider/client" - ibcproviderkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + appencoding "github.com/cosmos/interchain-security/v5/app/encoding" + testutil "github.com/cosmos/interchain-security/v5/testutil/integration" + "github.com/cosmos/interchain-security/v5/x/ccv/provider" + ibcprovider "github.com/cosmos/interchain-security/v5/x/ccv/provider" + ibcproviderclient "github.com/cosmos/interchain-security/v5/x/ccv/provider/client" + ibcproviderkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) const ( @@ -470,12 +471,15 @@ func New( app.BankKeeper, scopedTransferKeeper, ) - transferModule := transfer.NewAppModule(app.TransferKeeper) - ibcmodule := transfer.NewIBCModule(app.TransferKeeper) + + // Add an IBC middleware callback to track the consumer rewards + var transferStack porttypes.IBCModule + transferStack = transfer.NewIBCModule(app.TransferKeeper) + transferStack = provider.NewIBCMiddleware(transferStack, app.ProviderKeeper) // create static IBC router, add transfer route, then set and seal it ibcRouter := porttypes.NewRouter() - ibcRouter.AddRoute(ibctransfertypes.ModuleName, ibcmodule) + ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferStack) ibcRouter.AddRoute(providertypes.ModuleName, providerModule) app.IBCKeeper.SetRouter(ibcRouter) @@ -514,7 +518,7 @@ func New( evidence.NewAppModule(app.EvidenceKeeper), ibc.NewAppModule(app.IBCKeeper), params.NewAppModule(app.ParamsKeeper), - transferModule, + transfer.NewAppModule(app.TransferKeeper), providerModule, ) @@ -610,7 +614,7 @@ func New( params.NewAppModule(app.ParamsKeeper), evidence.NewAppModule(app.EvidenceKeeper), ibc.NewAppModule(app.IBCKeeper), - transferModule, + transfer.NewAppModule(app.TransferKeeper), ) app.sm.RegisterStoreDecoders() diff --git a/app/sovereign/app.go b/app/sovereign/app.go index 3d1a981e83..6dd9774f3b 100644 --- a/app/sovereign/app.go +++ b/app/sovereign/app.go @@ -79,6 +79,7 @@ import ( govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + // add mint mint "github.com/cosmos/cosmos-sdk/x/mint" mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" @@ -105,8 +106,8 @@ import ( "github.com/cometbft/cometbft/libs/log" tmos "github.com/cometbft/cometbft/libs/os" - appencoding "github.com/cosmos/interchain-security/v4/app/encoding" - testutil "github.com/cosmos/interchain-security/v4/testutil/integration" + appencoding "github.com/cosmos/interchain-security/v5/app/encoding" + testutil "github.com/cosmos/interchain-security/v5/testutil/integration" ) const ( diff --git a/cmd/interchain-security-cd/cmd/root.go b/cmd/interchain-security-cd/cmd/root.go index 215b45317a..3d844d9c7c 100644 --- a/cmd/interchain-security-cd/cmd/root.go +++ b/cmd/interchain-security-cd/cmd/root.go @@ -31,8 +31,8 @@ import ( tmcfg "github.com/cometbft/cometbft/config" "github.com/cometbft/cometbft/libs/log" - consumer "github.com/cosmos/interchain-security/v4/app/consumer" - appencoding "github.com/cosmos/interchain-security/v4/app/encoding" + consumer "github.com/cosmos/interchain-security/v5/app/consumer" + appencoding "github.com/cosmos/interchain-security/v5/app/encoding" ) // NewRootCmd creates a new root command for simd. It is called once in the diff --git a/cmd/interchain-security-cd/main.go b/cmd/interchain-security-cd/main.go index a64a2a8645..7cbe0a898a 100644 --- a/cmd/interchain-security-cd/main.go +++ b/cmd/interchain-security-cd/main.go @@ -6,9 +6,9 @@ import ( "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" - app "github.com/cosmos/interchain-security/v4/app/consumer" - appparams "github.com/cosmos/interchain-security/v4/app/params" - "github.com/cosmos/interchain-security/v4/cmd/interchain-security-cd/cmd" + app "github.com/cosmos/interchain-security/v5/app/consumer" + appparams "github.com/cosmos/interchain-security/v5/app/params" + "github.com/cosmos/interchain-security/v5/cmd/interchain-security-cd/cmd" ) func main() { diff --git a/cmd/interchain-security-cdd/cmd/root.go b/cmd/interchain-security-cdd/cmd/root.go index 6b2e6cc726..cb5140ade6 100644 --- a/cmd/interchain-security-cdd/cmd/root.go +++ b/cmd/interchain-security-cdd/cmd/root.go @@ -31,8 +31,8 @@ import ( tmcfg "github.com/cometbft/cometbft/config" "github.com/cometbft/cometbft/libs/log" - cdd "github.com/cosmos/interchain-security/v4/app/consumer-democracy" - appencoding "github.com/cosmos/interchain-security/v4/app/encoding" + cdd "github.com/cosmos/interchain-security/v5/app/consumer-democracy" + appencoding "github.com/cosmos/interchain-security/v5/app/encoding" ) // NewRootCmd creates a new root command for simd. It is called once in the diff --git a/cmd/interchain-security-cdd/main.go b/cmd/interchain-security-cdd/main.go index 9b6aacd759..9fbd09df75 100644 --- a/cmd/interchain-security-cdd/main.go +++ b/cmd/interchain-security-cdd/main.go @@ -6,9 +6,9 @@ import ( "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" - app "github.com/cosmos/interchain-security/v4/app/consumer-democracy" - appparams "github.com/cosmos/interchain-security/v4/app/params" - "github.com/cosmos/interchain-security/v4/cmd/interchain-security-cdd/cmd" + app "github.com/cosmos/interchain-security/v5/app/consumer-democracy" + appparams "github.com/cosmos/interchain-security/v5/app/params" + "github.com/cosmos/interchain-security/v5/cmd/interchain-security-cdd/cmd" ) func main() { diff --git a/cmd/interchain-security-pd/cmd/root.go b/cmd/interchain-security-pd/cmd/root.go index 8a8f5fec32..7f1b0a6b9a 100644 --- a/cmd/interchain-security-pd/cmd/root.go +++ b/cmd/interchain-security-pd/cmd/root.go @@ -31,8 +31,8 @@ import ( tmcfg "github.com/cometbft/cometbft/config" "github.com/cometbft/cometbft/libs/log" - appencoding "github.com/cosmos/interchain-security/v4/app/encoding" - providerApp "github.com/cosmos/interchain-security/v4/app/provider" + appencoding "github.com/cosmos/interchain-security/v5/app/encoding" + providerApp "github.com/cosmos/interchain-security/v5/app/provider" ) // NewRootCmd creates a new root command for simd. It is called once in the diff --git a/cmd/interchain-security-pd/main.go b/cmd/interchain-security-pd/main.go index 7788f06bff..dab89c91fc 100644 --- a/cmd/interchain-security-pd/main.go +++ b/cmd/interchain-security-pd/main.go @@ -6,9 +6,9 @@ import ( "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" - appparams "github.com/cosmos/interchain-security/v4/app/params" - app "github.com/cosmos/interchain-security/v4/app/provider" - "github.com/cosmos/interchain-security/v4/cmd/interchain-security-pd/cmd" + appparams "github.com/cosmos/interchain-security/v5/app/params" + app "github.com/cosmos/interchain-security/v5/app/provider" + "github.com/cosmos/interchain-security/v5/cmd/interchain-security-pd/cmd" ) func main() { diff --git a/cmd/interchain-security-sd/cmd/root.go b/cmd/interchain-security-sd/cmd/root.go index 28520ef1a4..10a79a251a 100644 --- a/cmd/interchain-security-sd/cmd/root.go +++ b/cmd/interchain-security-sd/cmd/root.go @@ -31,8 +31,8 @@ import ( tmcfg "github.com/cometbft/cometbft/config" "github.com/cometbft/cometbft/libs/log" - appencoding "github.com/cosmos/interchain-security/v4/app/encoding" - sovereignApp "github.com/cosmos/interchain-security/v4/app/sovereign" + appencoding "github.com/cosmos/interchain-security/v5/app/encoding" + sovereignApp "github.com/cosmos/interchain-security/v5/app/sovereign" ) // NewRootCmd creates a new root command for simd. It is called once in the diff --git a/cmd/interchain-security-sd/main.go b/cmd/interchain-security-sd/main.go index 2265afad90..e719eb48f3 100644 --- a/cmd/interchain-security-sd/main.go +++ b/cmd/interchain-security-sd/main.go @@ -6,9 +6,9 @@ import ( "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" - appparams "github.com/cosmos/interchain-security/v4/app/params" - app "github.com/cosmos/interchain-security/v4/app/sovereign" - "github.com/cosmos/interchain-security/v4/cmd/interchain-security-sd/cmd" + appparams "github.com/cosmos/interchain-security/v5/app/params" + app "github.com/cosmos/interchain-security/v5/app/sovereign" + "github.com/cosmos/interchain-security/v5/cmd/interchain-security-sd/cmd" ) func main() { diff --git a/go.mod b/go.mod index db8b94586c..4a5ddd9493 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/cosmos/interchain-security/v4 +module github.com/cosmos/interchain-security/v5 go 1.21.1 diff --git a/proto/interchain_security/ccv/consumer/v1/consumer.proto b/proto/interchain_security/ccv/consumer/v1/consumer.proto index 749eedc7ac..959d06c087 100644 --- a/proto/interchain_security/ccv/consumer/v1/consumer.proto +++ b/proto/interchain_security/ccv/consumer/v1/consumer.proto @@ -1,7 +1,7 @@ syntax = "proto3"; package interchain_security.ccv.consumer.v1; -option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types"; +option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types"; import "google/protobuf/any.proto"; import "gogoproto/gogo.proto"; diff --git a/proto/interchain_security/ccv/consumer/v1/genesis.proto b/proto/interchain_security/ccv/consumer/v1/genesis.proto index a2ceb0f9f6..642b78451d 100644 --- a/proto/interchain_security/ccv/consumer/v1/genesis.proto +++ b/proto/interchain_security/ccv/consumer/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package interchain_security.ccv.consumer.v1; -option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types"; +option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types"; import "interchain_security/ccv/v1/shared_consumer.proto"; import "ibc/lightclients/tendermint/v1/tendermint.proto"; diff --git a/proto/interchain_security/ccv/consumer/v1/query.proto b/proto/interchain_security/ccv/consumer/v1/query.proto index eb8eb29a3d..0e9b088e1d 100644 --- a/proto/interchain_security/ccv/consumer/v1/query.proto +++ b/proto/interchain_security/ccv/consumer/v1/query.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package interchain_security.ccv.consumer.v1; import "interchain_security/ccv/v1/shared_consumer.proto"; -option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types"; +option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types"; import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; diff --git a/proto/interchain_security/ccv/provider/v1/genesis.proto b/proto/interchain_security/ccv/provider/v1/genesis.proto index 443ea26b32..269743721e 100644 --- a/proto/interchain_security/ccv/provider/v1/genesis.proto +++ b/proto/interchain_security/ccv/provider/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package interchain_security.ccv.provider.v1; -option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"; +option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/provider/types"; import "gogoproto/gogo.proto"; import "interchain_security/ccv/v1/shared_consumer.proto"; diff --git a/proto/interchain_security/ccv/provider/v1/provider.proto b/proto/interchain_security/ccv/provider/v1/provider.proto index 4da89022e8..068cdc0f06 100644 --- a/proto/interchain_security/ccv/provider/v1/provider.proto +++ b/proto/interchain_security/ccv/provider/v1/provider.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package interchain_security.ccv.provider.v1; -option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"; +option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/provider/types"; import "interchain_security/ccv/v1/wire.proto"; import "gogoproto/gogo.proto"; @@ -13,6 +13,8 @@ import "ibc/lightclients/tendermint/v1/tendermint.proto"; import "tendermint/crypto/keys.proto"; import "cosmos/evidence/v1beta1/evidence.proto"; import "cosmos/base/v1beta1/coin.proto"; +import "amino/amino.proto"; + // // Note any type defined in this file is ONLY used internally to the provider CCV module. @@ -83,6 +85,25 @@ message ConsumerAdditionProposal { // chain. it is most relevant for chains performing a sovereign to consumer // changeover in order to maintain the existing ibc transfer channel string distribution_transmission_channel = 14; + // Corresponds to the percentage of validators that have to validate the chain under the Top N case. + // For example, 53 corresponds to a Top 53% chain, meaning that the top 53% provider validators by voting power + // have to validate the proposed consumer chain. top_N can either be 0 or any value in [50, 100]. + // A chain can join with top_N == 0 as an Opt In chain, or with top_N ∈ [50, 100] as a Top N chain. + uint32 top_N = 15; + // Corresponds to the maximum power (percentage-wise) a validator can have on the consumer chain. For instance, if + // `validators_power_cap` is set to 32, it means that no validator can have more than 32% of the voting power on the + // consumer chain. Note that this might not be feasible. For example, think of a consumer chain with only + // 5 validators and with `validators_power_cap` set to 10%. In such a scenario, at least one validator would need + // to have more than 20% of the total voting power. Therefore, `validators_power_cap` operates on a best-effort basis. + uint32 validators_power_cap = 16; + // Corresponds to the maximum number of validators that can validate a consumer chain. + // Only applicable to Opt In chains. Setting `validator_set_cap` on a Top N chain is a no-op. + uint32 validator_set_cap = 17; + // Corresponds to a list of provider consensus addresses of validators that are the ONLY ones that can validate + // the consumer chain. + repeated string allowlist = 18; + // Corresponds to a list of provider consensus addresses of validators that CANNOT validate the consumer chain. + repeated string denylist = 19; } // ConsumerRemovalProposal is a governance proposal on the provider chain to @@ -308,4 +329,14 @@ message ConsumerValidator { int64 power = 2; // public key the validator uses on the consumer chain during this epoch tendermint.crypto.PublicKey consumer_public_key = 3; -} \ No newline at end of file +} +// ConsumerRewardsAllocation stores the rewards allocated by a consumer chain +// to the consumer rewards pool. It is used to allocate the tokens to the consumer +// opted-in validators and the community pool during BeginBlock. +message ConsumerRewardsAllocation { + repeated cosmos.base.v1beta1.DecCoin rewards = 1 [ + (gogoproto.nullable) = false, + (amino.dont_omitempty) = true, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins" + ]; +} diff --git a/proto/interchain_security/ccv/provider/v1/query.proto b/proto/interchain_security/ccv/provider/v1/query.proto index 14a5ce41f6..f6d5d5a379 100644 --- a/proto/interchain_security/ccv/provider/v1/query.proto +++ b/proto/interchain_security/ccv/provider/v1/query.proto @@ -1,7 +1,7 @@ syntax = "proto3"; package interchain_security.ccv.provider.v1; -option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"; +option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/provider/types"; import "google/api/annotations.proto"; import "gogoproto/gogo.proto"; @@ -10,6 +10,7 @@ import "interchain_security/ccv/provider/v1/provider.proto"; import "interchain_security/ccv/v1/shared_consumer.proto"; import "interchain_security/ccv/v1/wire.proto"; import "tendermint/crypto/keys.proto"; +import "cosmos_proto/cosmos.proto"; service Query { // ConsumerGenesis queries the genesis state needed to start a consumer chain @@ -100,6 +101,33 @@ service Query { "/interchain_security/ccv/provider/params"; } + // QueryConsumerChainOptedInValidators returns a list of validators consensus addresses + // that opted-in to the given consumer chain + rpc QueryConsumerChainOptedInValidators( + QueryConsumerChainOptedInValidatorsRequest) + returns (QueryConsumerChainOptedInValidatorsResponse) { + option (google.api.http).get = + "/interchain_security/ccv/provider/opted_in_validators/{chain_id}"; + } + + // QueryConsumerChainsValidatorHasToValidate returns a list of consumer chains + // that a given validator must validate + rpc QueryConsumerChainsValidatorHasToValidate( + QueryConsumerChainsValidatorHasToValidateRequest) + returns (QueryConsumerChainsValidatorHasToValidateResponse) { + option (google.api.http).get = + "/interchain_security/ccv/provider/consumer_chains_per_validator/{provider_address}"; + } + + // QueryValidatorConsumerCommissionRate returns the commission rate a given + // validator charges on a given consumer chain + rpc QueryValidatorConsumerCommissionRate( + QueryValidatorConsumerCommissionRateRequest) + returns (QueryValidatorConsumerCommissionRateResponse) { + option (google.api.http).get = + "/interchain_security/ccv/provider/consumer_commission_rate/{chain_id}/{provider_address}"; + } + // QueryOldestUnconfirmedVsc returns the send timestamp of the oldest unconfirmed VSCPacket for a given chainID rpc QueryOldestUnconfirmedVsc(QueryOldestUnconfirmedVscRequest) returns (QueryOldestUnconfirmedVscResponse) { @@ -134,6 +162,8 @@ message QueryConsumerChainStopProposalsResponse { message Chain { string chain_id = 1; string client_id = 2; + // If chain with `chainID` is a Top-N chain, i.e., enforces at least one validator to validate chain `chainID` + uint32 top_N = 3; } message QueryValidatorConsumerAddrRequest { @@ -219,9 +249,44 @@ message QueryParamsResponse { Params params = 1 [(gogoproto.nullable) = false]; } + +message QueryConsumerChainOptedInValidatorsRequest { + string chain_id = 1; +} + +message QueryConsumerChainOptedInValidatorsResponse { + // The consensus addresses of the validators on the provider chain + repeated string validators_provider_addresses = 1; +} + + +message QueryConsumerChainsValidatorHasToValidateRequest { + // The consensus address of the validator on the provider chain + string provider_address = 1 [ (gogoproto.moretags) = "yaml:\"address\"" ]; +} + +message QueryConsumerChainsValidatorHasToValidateResponse { + repeated string consumer_chain_ids = 1; +} + +message QueryValidatorConsumerCommissionRateRequest { + string chain_id = 1; + // The consensus address of the validator on the provider chain + string provider_address = 2 [ (gogoproto.moretags) = "yaml:\"address\"" ]; +} + +message QueryValidatorConsumerCommissionRateResponse { + // The rate to charge delegators on the consumer chain, as a fraction + string rate = 1 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} + message QueryOldestUnconfirmedVscRequest { string chain_id = 1; } message QueryOldestUnconfirmedVscResponse { interchain_security.ccv.provider.v1.VscSendTimestamp vsc_send_timestamp = 1 [ (gogoproto.nullable) = false ]; -} \ No newline at end of file +} diff --git a/proto/interchain_security/ccv/provider/v1/tx.proto b/proto/interchain_security/ccv/provider/v1/tx.proto index 3294807015..02a69d25f9 100644 --- a/proto/interchain_security/ccv/provider/v1/tx.proto +++ b/proto/interchain_security/ccv/provider/v1/tx.proto @@ -1,7 +1,7 @@ syntax = "proto3"; package interchain_security.ccv.provider.v1; -option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"; +option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/provider/types"; import "gogoproto/gogo.proto"; import "cosmos_proto/cosmos.proto"; @@ -15,6 +15,9 @@ service Msg { rpc AssignConsumerKey(MsgAssignConsumerKey) returns (MsgAssignConsumerKeyResponse); rpc SubmitConsumerMisbehaviour(MsgSubmitConsumerMisbehaviour) returns (MsgSubmitConsumerMisbehaviourResponse); rpc SubmitConsumerDoubleVoting(MsgSubmitConsumerDoubleVoting) returns (MsgSubmitConsumerDoubleVotingResponse); + rpc OptIn(MsgOptIn) returns (MsgOptInResponse); + rpc OptOut(MsgOptOut) returns (MsgOptOutResponse); + rpc SetConsumerCommissionRate(MsgSetConsumerCommissionRate) returns (MsgSetConsumerCommissionRateResponse); } message MsgAssignConsumerKey { @@ -61,3 +64,50 @@ message MsgSubmitConsumerDoubleVoting { } message MsgSubmitConsumerDoubleVotingResponse {} + +message MsgOptIn { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + // the chain id of the consumer chain to opt in to + string chain_id = 1; + // the validator address on the provider + string provider_addr = 2 [ (gogoproto.moretags) = "yaml:\"address\"" ]; + // (optional) The consensus public key to use on the consumer in json string format corresponding to proto-any, + // for example `{"@type":"/cosmos.crypto.ed25519.PubKey","key":"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is="}`. + // This field is optional and can remain empty (i.e., `consumer_key = ""`). A validator can always change the + // consumer public key at a later stage by issuing a `MsgAssignConsumerKey` message. + string consumer_key = 3; +} + +message MsgOptInResponse {} + +message MsgOptOut { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + // the chain id of the consumer chain to opt out from + string chain_id = 1; + // the validator address on the provider + string provider_addr = 2 [ (gogoproto.moretags) = "yaml:\"address\"" ]; +} + +message MsgOptOutResponse {} + +// MsgSetConsumerCommissionRate allows validators to set +// a per-consumer chain commission rate +message MsgSetConsumerCommissionRate { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + // The validator address on the provider + string provider_addr = 1 [ (gogoproto.moretags) = "yaml:\"address\"" ]; + // The chain id of the consumer chain to set a commission rate + string chain_id = 2; + // The rate to charge delegators on the consumer chain, as a fraction + string rate = 3 [ + (cosmos_proto.scalar) = "cosmos.Dec", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} + + +message MsgSetConsumerCommissionRateResponse {} diff --git a/proto/interchain_security/ccv/v1/shared_consumer.proto b/proto/interchain_security/ccv/v1/shared_consumer.proto index d1f0a5d5a3..ce65df04a6 100644 --- a/proto/interchain_security/ccv/v1/shared_consumer.proto +++ b/proto/interchain_security/ccv/v1/shared_consumer.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package interchain_security.ccv.v1; -option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/types"; +option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/types"; import "tendermint/abci/types.proto"; import "ibc/lightclients/tendermint/v1/tendermint.proto"; diff --git a/proto/interchain_security/ccv/v1/wire.proto b/proto/interchain_security/ccv/v1/wire.proto index 7382b9d0da..f0ba6ab41a 100644 --- a/proto/interchain_security/ccv/v1/wire.proto +++ b/proto/interchain_security/ccv/v1/wire.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package interchain_security.ccv.v1; -option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/types"; +option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/types"; import "cosmos/staking/v1beta1/staking.proto"; diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh index 67ae158143..fdf6a894c1 100644 --- a/scripts/protocgen.sh +++ b/scripts/protocgen.sh @@ -16,6 +16,6 @@ done cd .. # move proto files to the right places -cp -r github.com/cosmos/interchain-security/v4/* ./ +cp -r github.com/cosmos/interchain-security/v5/* ./ rm -rf github.com diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 172ac70432..31813efd27 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -19,9 +19,9 @@ import ( "github.com/tidwall/gjson" "golang.org/x/mod/semver" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/client" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/client" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) const ( @@ -260,6 +260,7 @@ type SubmitConsumerAdditionProposalAction struct { SpawnTime uint InitialHeight clienttypes.Height DistributionChannel string + TopN uint32 } func (tr TestConfig) submitConsumerAdditionProposal( @@ -285,6 +286,7 @@ func (tr TestConfig) submitConsumerAdditionProposal( UnbondingPeriod: params.UnbondingPeriod, Deposit: fmt.Sprint(action.Deposit) + `stake`, DistributionTransmissionChannel: action.DistributionChannel, + TopN: action.TopN, } bz, err := json.Marshal(prop) @@ -298,9 +300,15 @@ func (tr TestConfig) submitConsumerAdditionProposal( } //#nosec G204 -- bypass unsafe quoting warning (no production code) - bz, err = target.ExecCommand( + cmd := target.ExecCommand( "/bin/bash", "-c", fmt.Sprintf(`echo '%s' > %s`, jsonStr, "/temp-proposal.json"), - ).CombinedOutput() + ) + bz, err = cmd.CombinedOutput() + + if verbose { + log.Println("submitConsumerAdditionProposal cmd: ", cmd.String()) + } + if err != nil { log.Fatal(err, "\n", string(bz)) } @@ -1498,11 +1506,9 @@ func (tr TestConfig) delegateTokens( } cmd := target.ExecCommand(tr.chainConfigs[action.Chain].BinaryName, - "tx", "staking", "delegate", validatorAddress, fmt.Sprint(action.Amount)+`stake`, - `--from`, `validator`+fmt.Sprint(action.From), `--chain-id`, string(tr.chainConfigs[action.Chain].ChainId), `--home`, tr.getValidatorHome(action.Chain, action.From), @@ -1689,7 +1695,6 @@ func (tr TestConfig) redelegateTokens(action RedelegateTokensAction, target Exec cmd := target.ExecCommand( tr.chainConfigs[action.Chain].BinaryName, - "tx", "staking", "redelegate", redelegateSrc, redelegateDst, @@ -2264,6 +2269,109 @@ func (tc TestConfig) startConsumerEvidenceDetector( tc.waitBlocks("provi", 10, 2*time.Minute) } +type OptInAction struct { + Chain ChainID + Validator ValidatorID +} + +func (tr TestConfig) optIn(action OptInAction, target ExecutionTarget, verbose bool) { + // Note: to get error response reported back from this command '--gas auto' needs to be set. + gas := "auto" + // Unfortunately, --gas auto does not work with CometMock. so when using CometMock, just use --gas 9000000 then + if tr.useCometmock { + gas = "9000000" + } + + // Use: "opt-in [consumer-chain-id] [consumer-pubkey]", + optIn := fmt.Sprintf( + `%s tx provider opt-in %s --from validator%s --chain-id %s --home %s --node %s --gas %s --keyring-backend test -y -o json`, + tr.chainConfigs[ChainID("provi")].BinaryName, + string(tr.chainConfigs[action.Chain].ChainId), + action.Validator, + tr.chainConfigs[ChainID("provi")].ChainId, + tr.getValidatorHome(ChainID("provi"), action.Validator), + tr.getValidatorNode(ChainID("provi"), action.Validator), + gas, + ) + + cmd := target.ExecCommand( + "/bin/bash", "-c", + optIn, + ) + + if verbose { + fmt.Println("optIn cmd:", cmd.String()) + } + + bz, err := cmd.CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + if !tr.useCometmock { // error report only works with --gas auto, which does not work with CometMock, so ignore + if err != nil && verbose { + fmt.Printf("got error during opt in | err: %s | output: %s \n", err, string(bz)) + } + } + + // wait for inclusion in a block -> '--broadcast-mode block' is deprecated + tr.waitBlocks(ChainID("provi"), 2, 30*time.Second) +} + +type OptOutAction struct { + Chain ChainID + Validator ValidatorID + ExpectError bool +} + +func (tr TestConfig) optOut(action OptOutAction, target ExecutionTarget, verbose bool) { + // Note: to get error response reported back from this command '--gas auto' needs to be set. + gas := "auto" + // Unfortunately, --gas auto does not work with CometMock. so when using CometMock, just use --gas 9000000 then + if tr.useCometmock { + gas = "9000000" + } + + // Use: "opt-out [consumer-chain-id]", + optIn := fmt.Sprintf( + `%s tx provider opt-out %s --from validator%s --chain-id %s --home %s --node %s --gas %s --keyring-backend test -y -o json`, + tr.chainConfigs[ChainID("provi")].BinaryName, + string(tr.chainConfigs[action.Chain].ChainId), + action.Validator, + tr.chainConfigs[ChainID("provi")].ChainId, + tr.getValidatorHome(ChainID("provi"), action.Validator), + tr.getValidatorNode(ChainID("provi"), action.Validator), + gas, + ) + + cmd := target.ExecCommand( + "/bin/bash", "-c", + optIn, + ) + + if verbose { + fmt.Println("optOut cmd:", cmd.String()) + } + + bz, err := cmd.CombinedOutput() + if action.ExpectError { + if err != nil { + if verbose { + fmt.Printf("got expected error during opt out | err: %s | output: %s \n", err, string(bz)) + } + } else { + log.Fatal("expected error during opt-out but got none") + } + } else { + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + } + + // wait for inclusion in a block -> '--broadcast-mode block' is deprecated + tr.waitBlocks(ChainID("provi"), 2, 30*time.Second) +} + // WaitTime waits for the given duration. // To make sure that the new timestamp is visible on-chain, it also waits until at least one block has been // produced on each chain after waiting. diff --git a/tests/e2e/builder.go b/tests/e2e/builder.go index 26eb5c34f6..e9e0f00d5a 100644 --- a/tests/e2e/builder.go +++ b/tests/e2e/builder.go @@ -82,7 +82,6 @@ func generateImageName(version string, cfg TargetConfig) (string, error) { tagName = "local" // this refers to build from local workspace } else { // use git hash of rev as docker image tag - // cmd := exec.Command("git", "rev-parse", "--verify", "--short", version) cmd := exec.Command("git", "log", "--pretty=format:%h", "-n", "1", version) out, err := cmd.CombinedOutput() if err != nil { diff --git a/tests/e2e/main.go b/tests/e2e/main.go index 83a41a0342..c1766ccf37 100644 --- a/tests/e2e/main.go +++ b/tests/e2e/main.go @@ -156,6 +156,18 @@ var stepChoices = map[string]StepChoice{ description: `Minimal set of test steps to perform compatibility tests`, testConfig: CompatibilityTestCfg, }, + "partial-set-security-opt-in": { + name: "partial-set-security-opt-in", + steps: stepsOptInChain(), + description: "test partial set security for an Opt-In chain", + testConfig: DefaultTestCfg, + }, + "partial-set-security-top-n": { + name: "partial-set-security-top-n", + steps: stepsTopNChain(), + description: "test partial set security for a Top-N chain", + testConfig: DefaultTestCfg, + }, } func getTestCaseUsageString() string { @@ -241,7 +253,7 @@ func getTestCases(selectedPredefinedTests, selectedTestFiles TestSet, providerVe "changeover", "happy-path", "democracy-reward", "democracy", "slash-throttle", "consumer-double-sign", "consumer-misbehaviour", - "consumer-double-downtime", + "consumer-double-downtime", "partial-set-security-opt-in", "partial-set-security-top-n", } if includeMultiConsumer != nil && *includeMultiConsumer { selectedPredefinedTests = append(selectedPredefinedTests, "multiconsumer") diff --git a/tests/e2e/state.go b/tests/e2e/state.go index 91379a0705..7b674b0df8 100644 --- a/tests/e2e/state.go +++ b/tests/e2e/state.go @@ -31,6 +31,7 @@ type ChainState struct { ConsumerPendingPacketQueueSize *uint // Only relevant to consumer chains RegisteredConsumerRewardDenoms *[]string ClientsFrozenHeights *map[string]clienttypes.Height + HasToValidate *map[ValidatorID][]ChainID // only relevant to provider chain } type Proposal interface { @@ -180,6 +181,14 @@ func (tr TestConfig) getChainState(chain ChainID, modelState ChainState) ChainSt chainState.ClientsFrozenHeights = &chainClientsFrozenHeights } + if modelState.HasToValidate != nil { + hasToValidate := map[ValidatorID][]ChainID{} + for validatorId := range *modelState.HasToValidate { + hasToValidate[validatorId] = tr.getHasToValidate(validatorId) + } + chainState.HasToValidate = &hasToValidate + } + if modelState.ConsumerPendingPacketQueueSize != nil { pendingPacketQueueSize := tr.getPendingPacketQueueSize(chain) chainState.ConsumerPendingPacketQueueSize = &pendingPacketQueueSize @@ -833,6 +842,29 @@ func (tc TestConfig) getClientFrozenHeight(chain ChainID, clientID string) clien return clienttypes.Height{RevisionHeight: uint64(revHeight), RevisionNumber: uint64(revNumber)} } +func (tr TestConfig) getHasToValidate( + validatorId ValidatorID, +) []ChainID { + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + bz, err := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[ChainID("provi")].BinaryName, + "query", "provider", "has-to-validate", + tr.validatorConfigs[validatorId].ValconsAddress, + `--node`, tr.getQueryNode(ChainID("provi")), + `-o`, `json`, + ).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + arr := gjson.Get(string(bz), "consumer_chain_ids").Array() + chains := []ChainID{} + for _, c := range arr { + chains = append(chains, ChainID(c.String())) + } + + return chains +} + func (tc TestConfig) getTrustedHeight( chain ChainID, clientID string, diff --git a/tests/e2e/steps_compatibility.go b/tests/e2e/steps_compatibility.go index 89e80f3600..c6bc3c88b1 100644 --- a/tests/e2e/steps_compatibility.go +++ b/tests/e2e/steps_compatibility.go @@ -41,6 +41,7 @@ func compstepsStartConsumerChain(consumerName string, proposalIndex, chainIndex ConsumerChain: ChainID(consumerName), SpawnTime: 0, InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + TopN: 100, }, State: State{ ChainID("provi"): ChainState{ @@ -85,18 +86,6 @@ func compstepsStartConsumerChain(consumerName string, proposalIndex, chainIndex }, }, }, - { - // op should fail - key already assigned by the same validator - Action: AssignConsumerPubKeyAction{ - Chain: ChainID(consumerName), - Validator: ValidatorID("carol"), - ConsumerPubkey: getDefaultValidators()[ValidatorID("carol")].ConsumerValPubKey, - ReconfigureNode: false, - ExpectError: true, - ExpectedError: "a validator has assigned the consumer key already: consumer key is already in use by a validator", - }, - State: State{}, - }, { // op should fail - key already assigned by another validator Action: AssignConsumerPubKeyAction{ diff --git a/tests/e2e/steps_consumer_misbehaviour.go b/tests/e2e/steps_consumer_misbehaviour.go index 3cb4ce0f1b..bd6e3f1d3c 100644 --- a/tests/e2e/steps_consumer_misbehaviour.go +++ b/tests/e2e/steps_consumer_misbehaviour.go @@ -36,6 +36,7 @@ func stepsStartChainsWithSoftOptOut(consumerName string) []Step { ConsumerChain: ChainID(consumerName), SpawnTime: 0, InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + TopN: 100, }, State: State{ ChainID("provi"): ChainState{ diff --git a/tests/e2e/steps_partial_set_security.go b/tests/e2e/steps_partial_set_security.go new file mode 100644 index 0000000000..4c63e479df --- /dev/null +++ b/tests/e2e/steps_partial_set_security.go @@ -0,0 +1,1002 @@ +package main + +import clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + +// stepsOptInChain starts a provider chain and an Opt-In chain and opts in and out validators +func stepsOptInChain() []Step { + s := []Step{ + { + Action: StartChainAction{ + Chain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + }, + }, + { + Action: SubmitConsumerAdditionProposalAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + Deposit: 10000001, + ConsumerChain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + TopN: 0, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerAdditionProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_VOTING_PERIOD", + }, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + // Οpt in "alice" and "bob" so the chain is not empty when it is about to start. Note, that "alice" and "bob" use + // the provider's public key (see `UseConsumerKey` is set to `false` in `getDefaultValidators`) and hence do not + // need a consumer-key assigment. + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("alice"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, // chain is not running yet + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("bob"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + Action: VoteGovProposalAction{ + Chain: ChainID("provi"), + From: []ValidatorID{ValidatorID("alice"), ValidatorID("bob")}, + Vote: []string{"yes", "yes"}, + PropNumber: 1, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerAdditionProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_PASSED", + }, + }, + }, + }, + }, + { + // we start all the validators but only "alice" and "bob" have opted in and hence + // only "alice" and "bob" are validating blocks + Action: StartConsumerChainAction{ + ConsumerChain: ChainID("consu"), + ProviderChain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + // For consumers that're launching with the provider being on an earlier version + // of ICS before the soft opt-out threshold was introduced, we need to set the + // soft opt-out threshold to 0.05 in the consumer genesis to ensure that the + // consumer binary doesn't panic. Sdk requires that all params are set to valid + // values from the genesis file. + GenesisChanges: ".app_state.ccvconsumer.params.soft_opt_out_threshold = \"0.05\"", + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + // carol has not yet opted in + ValidatorID("carol"): 0, + }, + }, + }, + }, + { + Action: AddIbcConnectionAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ClientA: 0, + ClientB: 0, + }, + State: State{}, + }, + { + Action: AddIbcChannelAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ConnectionA: 0, + PortA: "consumer", + PortB: "provider", + Order: "ordered", + }, + State: State{}, + }, + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + // "carol" has opted in, but the VSCPacket capturing the opt-in was not relayed yet + ValidatorID("carol"): 0, + }, + }, + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + { + // assign the consumer key "carol" is using on the consumer chain to be the one "carol" uses when opting in + Action: AssignConsumerPubKeyAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + // reconfigure the node -> validator was using provider key + // until this point -> key matches config.consumerValPubKey for "carol" + ConsumerPubkey: getDefaultValidators()[ValidatorID("carol")].ConsumerValPubKey, + ReconfigureNode: true, + }, + State: State{}, + }, + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + // carol has now opted in + ValidatorID("carol"): 300, + }, + }, + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + { + Action: OptOutAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("bob"), + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + // "bob" has not yet opted out from the consumer chain because the VSCPacket has not yet been relayed + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + }, + }, + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + // bob has now opted out + ValidatorID("bob"): 0, + ValidatorID("carol"): 300, + }, + }, + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + { + // re opt-in "bob" + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("bob"), + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + // "bob" has not yet been opted in to the consumer chain because the VSCPacket has not yet been relayed + ValidatorID("bob"): 0, + ValidatorID("carol"): 300, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {"consu"}, // but has to validate is true because bob opted in on the provider + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + // bob is in power on the consumer + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + }, + }, + { + // DowntimeSlash for alice on consumer + Action: DowntimeSlashAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("alice"), + }, + // powers are not affected yet on either chain + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + { + // relay the slash packet + Action: RelayPacketsAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + Port: "consumer", + Channel: 0, + }, + // alice's power is reduced on the provider + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, // alice is jailed on the provider and does not have to validate + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + { + // relay the VSCPacket that contains the slashed power for alice + Action: RelayPacketsAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + Port: "consumer", + Channel: 0, + }, + // alice's power is reduced on the provider + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, // alice is jailed on the provider and does not have to validate + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + }, + }, + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + // alice should definitely not be in power on the consumer + ValidatorID("alice"): 0, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + }, + }, + { + // unjail alice + Action: UnjailValidatorAction{ + Provider: ChainID("provi"), + Validator: ValidatorID("alice"), + }, + // alices power is restored on the provider + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, // alice is unjailed and hence has to validate again + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + }, + }, + // still 0 power on the consumer + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + }, + }, + { + // relay the VSCPacket that puts alice back into power on the consumer + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + // alice's power is restored on the consumer + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + }, + }, + { + // slash alice for downtime again + Action: DowntimeSlashAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("alice"), + }, + // alice's power is not yet reduced, the packet needs to be relayed + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + { + // relay the slash packet + Action: RelayPacketsAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + Port: "consumer", + Channel: 0, + }, + // alice's power is reduced on the provider + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, // alice is jailed on the provider and does not have to validate + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + { + // relay the VSCPacket that contains the slashed power for alice + Action: RelayPacketsAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + Port: "consumer", + Channel: 0, + }, + // alice's power is reduced on the consumer + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, // alice is jailed on the provider and does not have to validate + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + } + + return s +} + +// stepsTopNChain starts a provider chain and a Top-N chain and opts in and out validators +func stepsTopNChain() []Step { + s := []Step{ + { + // start a new chain where "alice", "bob", and "carol" have 20%, 30%, and 50% of + // the total voting power respectively + Action: StartChainAction{ + Chain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 300000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 500000000, Allocation: 10000000000}, + }, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 200, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + }, + }, + { + // propose a Top-N chain with N = 51% and hence both "carol" and "bob" have to validate + Action: SubmitConsumerAdditionProposalAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + Deposit: 10000001, + ConsumerChain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + TopN: 51, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerAdditionProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_VOTING_PERIOD", + }, + }, + }, + }, + }, + { + // change the consumer key "carol" is using on the consumer chain to be the one "carol" uses when opting in + Action: AssignConsumerPubKeyAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + // reconfigure the node -> validator was using provider key + // until this point -> key matches config.consumerValPubKey for "bob" + ConsumerPubkey: getDefaultValidators()[ValidatorID("carol")].ConsumerValPubKey, + ReconfigureNode: true, + }, + State: State{}, + }, + { + Action: VoteGovProposalAction{ + Chain: ChainID("provi"), + From: []ValidatorID{ValidatorID("alice"), ValidatorID("bob")}, + Vote: []string{"yes", "yes"}, + PropNumber: 1, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerAdditionProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_PASSED", + }, + }, + }, + }, + }, + { + // we start all the validators but only "alice" and "bob" have opted in and hence + // only "alice" and "bob" are validating blocks + Action: StartConsumerChainAction{ + ConsumerChain: ChainID("consu"), + ProviderChain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 300000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 500000000, Allocation: 10000000000}, + }, + // For consumers that're launching with the provider being on an earlier version + // of ICS before the soft opt-out threshold was introduced, we need to set the + // soft opt-out threshold to 0.05 in the consumer genesis to ensure that the + // consumer binary doesn't panic. Sdk requires that all params are set to valid + // values from the genesis file. + GenesisChanges: ".app_state.ccvconsumer.params.soft_opt_out_threshold = \"0.05\"", + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + }, + }, + { + Action: AddIbcConnectionAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ClientA: 0, + ClientB: 0, + }, + State: State{}, + }, + { + Action: AddIbcChannelAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ConnectionA: 0, + PortA: "consumer", + PortB: "provider", + Order: "ordered", + }, + State: State{}, + }, + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("alice"), + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + // "alice" is not yet opted in because the VSCPacket has not yet been relayed + ValidatorID("alice"): 0, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + // "alice" is now opted in + ValidatorID("alice"): 200, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + }, + }, + { + Action: OptOutAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + ExpectError: true, + }, + State: State{}, + }, + { + Action: OptOutAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("bob"), + ExpectError: true, + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + { + // opting out "bob" or "carol" does not work because they belong to the Top N validators + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 200, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + { + Action: OptOutAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("alice"), + }, + State: State{}, + }, + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + // "alice" has now opted out + ValidatorID("alice"): 0, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, // alice has opted out and the epoch is over, so definitely does not have to validate anymore + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + // opt alice back in + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("alice"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, // alice has to validate again + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + // "alice" has now opted in + ValidatorID("alice"): 200, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + { + // DowntimeSlash for alice on consumer + Action: DowntimeSlashAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("alice"), + }, + // powers are not affected yet on either chain + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 200, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 200, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + }, + }, + { + // relay the slash packet + Action: RelayPacketsAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + Port: "consumer", + Channel: 0, + }, + // alice's power is reduced on the provider + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + }, + }, + { + // relay the VSCPacket that contains the slashed power for alice + Action: RelayPacketsAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + Port: "consumer", + Channel: 0, + }, + // alice's power is reduced on the provider + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + // alice should definitely not be in power on the consumer + ValidatorID("alice"): 0, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + }, + }, + { + // unjail alice + Action: UnjailValidatorAction{ + Provider: ChainID("provi"), + Validator: ValidatorID("alice"), + }, + // alices power is restored on the provider + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 200, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + // still 0 power on the consumer + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + }, + }, + { + // relay the VSCPacket that puts alice back into power on the consumer + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + // alice's power is restored on the consumer + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 200, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + }, + }, + { + // slash alice for downtime again + Action: DowntimeSlashAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("alice"), + }, + // alice's power is not yet reduced, the packet needs to be relayed + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 200, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 200, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + }, + }, + { + // relay the slash packet + Action: RelayPacketsAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + Port: "consumer", + Channel: 0, + }, + // alice's power is reduced on the provider + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + }, + }, + { + // relay the VSCPacket that contains the slashed power for alice + Action: RelayPacketsAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + Port: "consumer", + Channel: 0, + }, + // alice's power is reduced on the consumer + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 300, + ValidatorID("carol"): 500, + }, + }, + }, + }, + } + + return s +} diff --git a/tests/e2e/steps_sovereign_changeover.go b/tests/e2e/steps_sovereign_changeover.go index 69e6680677..1ddd5a2143 100644 --- a/tests/e2e/steps_sovereign_changeover.go +++ b/tests/e2e/steps_sovereign_changeover.go @@ -48,6 +48,7 @@ func stepsChangeoverToConsumer(consumerName string) []Step { DistributionChannel: "channel-0", SpawnTime: 0, InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 111}, // 1 block after upgrade !important + TopN: 100, }, State: State{ ChainID("provi"): ChainState{ diff --git a/tests/e2e/steps_start_chains.go b/tests/e2e/steps_start_chains.go index 08732e3f37..2ff3226dec 100644 --- a/tests/e2e/steps_start_chains.go +++ b/tests/e2e/steps_start_chains.go @@ -38,6 +38,7 @@ func stepsStartConsumerChain(consumerName string, proposalIndex, chainIndex uint ConsumerChain: ChainID(consumerName), SpawnTime: 0, InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + TopN: 100, }, State: State{ ChainID("provi"): ChainState{ @@ -82,16 +83,24 @@ func stepsStartConsumerChain(consumerName string, proposalIndex, chainIndex uint }, }, { - // op should fail - key already assigned by the same validator + // op should be a noop - key already assigned, but by the same validator Action: AssignConsumerPubKeyAction{ Chain: ChainID(consumerName), Validator: ValidatorID("carol"), ConsumerPubkey: getDefaultValidators()[ValidatorID("carol")].ConsumerValPubKey, ReconfigureNode: false, - ExpectError: true, - ExpectedError: "a validator has assigned the consumer key already: consumer key is already in use by a validator", + ExpectError: false, + }, + State: State{ + ChainID(consumerName): ChainState{ + AssignedKeys: &map[ValidatorID]string{ + ValidatorID("carol"): getDefaultValidators()[ValidatorID("carol")].ConsumerValconsAddressOnProvider, + }, + ProviderKeys: &map[ValidatorID]string{ + ValidatorID("carol"): getDefaultValidators()[ValidatorID("carol")].ValconsAddress, + }, + }, }, - State: State{}, }, { // op should fail - key already assigned by another validator diff --git a/tests/e2e/test_driver.go b/tests/e2e/test_driver.go index 052559b3ba..a12dc7862e 100644 --- a/tests/e2e/test_driver.go +++ b/tests/e2e/test_driver.go @@ -138,6 +138,10 @@ func (td *DefaultDriver) runAction(action interface{}) error { td.testCfg.startConsumerEvidenceDetector(action, td.target, td.verbose) case SubmitChangeRewardDenomsProposalAction: td.testCfg.submitChangeRewardDenomsProposal(action, td.target, td.verbose) + case OptInAction: + td.testCfg.optIn(action, td.target, td.verbose) + case OptOutAction: + td.testCfg.optOut(action, td.target, td.verbose) default: log.Fatalf("unknown action in testRun %s: %#v", td.testCfg.name, action) } diff --git a/tests/e2e/tracehandler_testdata/changeover.json b/tests/e2e/tracehandler_testdata/changeover.json index 47d6fc621d..1b6ae37ae1 100644 --- a/tests/e2e/tracehandler_testdata/changeover.json +++ b/tests/e2e/tracehandler_testdata/changeover.json @@ -28,6 +28,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -58,6 +59,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -104,6 +106,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -151,6 +154,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -193,6 +197,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -229,7 +234,8 @@ "InitialHeight": { "revision_height": 111 }, - "DistributionChannel": "channel-0" + "DistributionChannel": "channel-0", + "TopN": 100 }, "State": { "provi": { @@ -248,6 +254,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -298,6 +305,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -357,6 +365,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "sover": { @@ -376,6 +385,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -427,6 +437,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -457,6 +468,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "sover": { @@ -476,6 +488,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -506,6 +519,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -534,6 +548,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -564,6 +579,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "sover": { @@ -583,6 +599,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -613,6 +630,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } diff --git a/tests/e2e/tracehandler_testdata/consumer-double-sign.json b/tests/e2e/tracehandler_testdata/consumer-double-sign.json index e93049cf8b..290f351875 100644 --- a/tests/e2e/tracehandler_testdata/consumer-double-sign.json +++ b/tests/e2e/tracehandler_testdata/consumer-double-sign.json @@ -41,6 +41,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -57,7 +58,8 @@ "InitialHeight": { "revision_height": 1 }, - "DistributionChannel": "" + "DistributionChannel": "", + "TopN": 100 }, "State": { "provi": { @@ -78,6 +80,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -123,6 +126,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -134,10 +138,31 @@ "Validator": "carol", "ConsumerPubkey": "{\"@type\":\"/cosmos.crypto.ed25519.PubKey\",\"key\":\"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=\"}", "ReconfigureNode": false, - "ExpectError": true, - "ExpectedError": "a validator has assigned the consumer key already: consumer key is already in use by a validator" + "ExpectError": false, + "ExpectedError": "" }, - "State": {} + "State": { + "consu": { + "ValBalances": null, + "ProposedConsumerChains": null, + "ValPowers": null, + "StakedTokens": null, + "Params": null, + "Rewards": null, + "ConsumerChains": null, + "AssignedKeys": { + "carol": "cosmosvalcons1kswr5sq599365kcjmhgufevfps9njf43e4lwdk" + }, + "ProviderKeys": { + "carol": "cosmosvalcons1ezyrq65s3gshhx5585w6mpusq3xsj3ayzf4uv6" + }, + "ConsumerPendingPacketQueueSize": null, + "RegisteredConsumerRewardDenoms": null, + "ClientsFrozenHeights": null, + "HasToValidate": null, + "Proposals": null + } + } }, { "ActionType": "main.AssignConsumerPubKeyAction", @@ -168,6 +193,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -205,6 +231,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -264,6 +291,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -283,6 +311,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -334,6 +363,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -357,6 +387,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -384,6 +415,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -407,6 +439,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -437,6 +470,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -460,6 +494,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } diff --git a/tests/e2e/tracehandler_testdata/consumer-misbehaviour.json b/tests/e2e/tracehandler_testdata/consumer-misbehaviour.json index 43e242e7dc..b0c64232e2 100644 --- a/tests/e2e/tracehandler_testdata/consumer-misbehaviour.json +++ b/tests/e2e/tracehandler_testdata/consumer-misbehaviour.json @@ -35,6 +35,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -51,7 +52,8 @@ "InitialHeight": { "revision_height": 1 }, - "DistributionChannel": "" + "DistributionChannel": "", + "TopN": 100 }, "State": { "provi": { @@ -70,6 +72,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -115,6 +118,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -150,6 +154,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -197,6 +202,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -215,6 +221,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -267,6 +274,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -285,6 +293,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -314,6 +323,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -355,6 +365,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -376,6 +387,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -405,6 +417,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -430,6 +443,7 @@ "revision_height": 1 } }, + "HasToValidate": null, "Proposals": null } } diff --git a/tests/e2e/tracehandler_testdata/democracy.json b/tests/e2e/tracehandler_testdata/democracy.json index 10c9838122..bcfcd0bcd4 100644 --- a/tests/e2e/tracehandler_testdata/democracy.json +++ b/tests/e2e/tracehandler_testdata/democracy.json @@ -41,6 +41,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -57,7 +58,8 @@ "InitialHeight": { "revision_height": 1 }, - "DistributionChannel": "" + "DistributionChannel": "", + "TopN": 100 }, "State": { "provi": { @@ -78,6 +80,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -123,6 +126,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -134,10 +138,31 @@ "Validator": "carol", "ConsumerPubkey": "{\"@type\":\"/cosmos.crypto.ed25519.PubKey\",\"key\":\"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=\"}", "ReconfigureNode": false, - "ExpectError": true, - "ExpectedError": "a validator has assigned the consumer key already: consumer key is already in use by a validator" + "ExpectError": false, + "ExpectedError": "" }, - "State": {} + "State": { + "democ": { + "ValBalances": null, + "ProposedConsumerChains": null, + "ValPowers": null, + "StakedTokens": null, + "Params": null, + "Rewards": null, + "ConsumerChains": null, + "AssignedKeys": { + "carol": "cosmosvalcons1kswr5sq599365kcjmhgufevfps9njf43e4lwdk" + }, + "ProviderKeys": { + "carol": "cosmosvalcons1ezyrq65s3gshhx5585w6mpusq3xsj3ayzf4uv6" + }, + "ConsumerPendingPacketQueueSize": null, + "RegisteredConsumerRewardDenoms": null, + "ClientsFrozenHeights": null, + "HasToValidate": null, + "Proposals": null + } + } }, { "ActionType": "main.AssignConsumerPubKeyAction", @@ -168,6 +193,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -205,6 +231,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -264,6 +291,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -283,6 +311,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -350,6 +379,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -369,6 +399,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -398,6 +429,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -428,6 +460,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -457,6 +490,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -499,6 +533,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -540,6 +575,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -577,6 +613,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -629,6 +666,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -674,6 +712,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": [], "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -699,6 +738,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": [], "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -735,6 +775,7 @@ "ibc/3C3D7B3BE4ECC85A0E5B52A3AEC3B7DFC2AA9CA47C37821E57020D6807043BE9" ], "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -769,6 +810,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -797,6 +839,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -816,6 +859,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -846,6 +890,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -865,6 +910,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -895,6 +941,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -923,6 +970,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -942,6 +990,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -975,6 +1024,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } diff --git a/tests/e2e/tracehandler_testdata/democracyRewardsSteps.json b/tests/e2e/tracehandler_testdata/democracyRewardsSteps.json index 7e6d90cace..83b1f326d9 100644 --- a/tests/e2e/tracehandler_testdata/democracyRewardsSteps.json +++ b/tests/e2e/tracehandler_testdata/democracyRewardsSteps.json @@ -41,6 +41,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -57,7 +58,8 @@ "InitialHeight": { "revision_height": 1 }, - "DistributionChannel": "" + "DistributionChannel": "", + "TopN": 100 }, "State": { "provi": { @@ -78,6 +80,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -123,6 +126,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -134,10 +138,31 @@ "Validator": "carol", "ConsumerPubkey": "{\"@type\":\"/cosmos.crypto.ed25519.PubKey\",\"key\":\"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=\"}", "ReconfigureNode": false, - "ExpectError": true, - "ExpectedError": "a validator has assigned the consumer key already: consumer key is already in use by a validator" + "ExpectError": false, + "ExpectedError": "" }, - "State": {} + "State": { + "democ": { + "ValBalances": null, + "ProposedConsumerChains": null, + "ValPowers": null, + "StakedTokens": null, + "Params": null, + "Rewards": null, + "ConsumerChains": null, + "AssignedKeys": { + "carol": "cosmosvalcons1kswr5sq599365kcjmhgufevfps9njf43e4lwdk" + }, + "ProviderKeys": { + "carol": "cosmosvalcons1ezyrq65s3gshhx5585w6mpusq3xsj3ayzf4uv6" + }, + "ConsumerPendingPacketQueueSize": null, + "RegisteredConsumerRewardDenoms": null, + "ClientsFrozenHeights": null, + "HasToValidate": null, + "Proposals": null + } + } }, { "ActionType": "main.AssignConsumerPubKeyAction", @@ -168,6 +193,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -205,6 +231,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -264,6 +291,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -283,6 +311,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -350,6 +379,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -369,6 +399,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -398,6 +429,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -428,6 +460,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -457,6 +490,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -499,6 +533,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -540,6 +575,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -577,6 +613,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -629,6 +666,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -674,6 +712,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": [], "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -699,6 +738,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": [], "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -735,6 +775,7 @@ "ibc/3C3D7B3BE4ECC85A0E5B52A3AEC3B7DFC2AA9CA47C37821E57020D6807043BE9" ], "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -769,6 +810,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -797,6 +839,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -816,6 +859,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -846,6 +890,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -865,6 +910,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -895,6 +941,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -923,6 +970,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -942,6 +990,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -975,6 +1024,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } diff --git a/tests/e2e/tracehandler_testdata/happyPath.json b/tests/e2e/tracehandler_testdata/happyPath.json index 5b9505e848..0109d72501 100644 --- a/tests/e2e/tracehandler_testdata/happyPath.json +++ b/tests/e2e/tracehandler_testdata/happyPath.json @@ -41,6 +41,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -57,7 +58,8 @@ "InitialHeight": { "revision_height": 1 }, - "DistributionChannel": "" + "DistributionChannel": "", + "TopN": 100 }, "State": { "provi": { @@ -78,6 +80,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -123,6 +126,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -134,10 +138,31 @@ "Validator": "carol", "ConsumerPubkey": "{\"@type\":\"/cosmos.crypto.ed25519.PubKey\",\"key\":\"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=\"}", "ReconfigureNode": false, - "ExpectError": true, - "ExpectedError": "a validator has assigned the consumer key already: consumer key is already in use by a validator" + "ExpectError": false, + "ExpectedError": "" }, - "State": {} + "State": { + "consu": { + "ValBalances": null, + "ProposedConsumerChains": null, + "ValPowers": null, + "StakedTokens": null, + "Params": null, + "Rewards": null, + "ConsumerChains": null, + "AssignedKeys": { + "carol": "cosmosvalcons1kswr5sq599365kcjmhgufevfps9njf43e4lwdk" + }, + "ProviderKeys": { + "carol": "cosmosvalcons1ezyrq65s3gshhx5585w6mpusq3xsj3ayzf4uv6" + }, + "ConsumerPendingPacketQueueSize": null, + "RegisteredConsumerRewardDenoms": null, + "ClientsFrozenHeights": null, + "HasToValidate": null, + "Proposals": null + } + } }, { "ActionType": "main.AssignConsumerPubKeyAction", @@ -168,6 +193,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -205,6 +231,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -264,6 +291,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -283,6 +311,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -336,6 +365,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -355,6 +385,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -384,6 +415,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -414,6 +446,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -443,6 +476,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -481,6 +515,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -500,6 +535,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -536,6 +572,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -555,6 +592,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -585,6 +623,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -604,6 +643,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -634,6 +674,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -664,6 +705,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -683,6 +725,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -713,6 +756,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -743,6 +787,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -762,6 +807,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -792,6 +838,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -823,6 +870,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -842,6 +890,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -872,6 +921,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -900,6 +950,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -919,6 +970,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -949,6 +1001,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -968,6 +1021,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -999,6 +1053,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1018,6 +1073,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1048,6 +1104,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1076,6 +1133,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1095,6 +1153,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1124,6 +1183,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1143,6 +1203,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1173,6 +1234,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1201,6 +1263,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1220,6 +1283,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1250,6 +1314,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1278,6 +1343,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1297,6 +1363,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1327,6 +1394,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1355,6 +1423,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1374,6 +1443,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1404,6 +1474,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1432,6 +1503,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1451,6 +1523,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1481,6 +1554,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1500,6 +1574,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1536,6 +1611,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "2": { "RawProposal": { @@ -1584,6 +1660,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "2": { "RawProposal": { @@ -1625,6 +1702,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "3": { "RawProposal": { @@ -1671,6 +1749,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "3": { "RawProposal": { diff --git a/tests/e2e/tracehandler_testdata/multipleConsumers.json b/tests/e2e/tracehandler_testdata/multipleConsumers.json index fdb69d1e47..b8bffb9cde 100644 --- a/tests/e2e/tracehandler_testdata/multipleConsumers.json +++ b/tests/e2e/tracehandler_testdata/multipleConsumers.json @@ -41,6 +41,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -57,7 +58,8 @@ "InitialHeight": { "revision_height": 1 }, - "DistributionChannel": "" + "DistributionChannel": "", + "TopN": 100 }, "State": { "provi": { @@ -78,6 +80,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -123,6 +126,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -134,10 +138,31 @@ "Validator": "carol", "ConsumerPubkey": "{\"@type\":\"/cosmos.crypto.ed25519.PubKey\",\"key\":\"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=\"}", "ReconfigureNode": false, - "ExpectError": true, - "ExpectedError": "a validator has assigned the consumer key already: consumer key is already in use by a validator" + "ExpectError": false, + "ExpectedError": "" }, - "State": {} + "State": { + "consu": { + "ValBalances": null, + "ProposedConsumerChains": null, + "ValPowers": null, + "StakedTokens": null, + "Params": null, + "Rewards": null, + "ConsumerChains": null, + "AssignedKeys": { + "carol": "cosmosvalcons1kswr5sq599365kcjmhgufevfps9njf43e4lwdk" + }, + "ProviderKeys": { + "carol": "cosmosvalcons1ezyrq65s3gshhx5585w6mpusq3xsj3ayzf4uv6" + }, + "ConsumerPendingPacketQueueSize": null, + "RegisteredConsumerRewardDenoms": null, + "ClientsFrozenHeights": null, + "HasToValidate": null, + "Proposals": null + } + } }, { "ActionType": "main.AssignConsumerPubKeyAction", @@ -168,6 +193,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -205,6 +231,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -264,6 +291,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -283,6 +311,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -322,7 +351,8 @@ "InitialHeight": { "revision_height": 1 }, - "DistributionChannel": "" + "DistributionChannel": "", + "TopN": 100 }, "State": { "provi": { @@ -343,6 +373,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "2": { "RawProposal": { @@ -388,6 +419,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -399,10 +431,31 @@ "Validator": "carol", "ConsumerPubkey": "{\"@type\":\"/cosmos.crypto.ed25519.PubKey\",\"key\":\"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=\"}", "ReconfigureNode": false, - "ExpectError": true, - "ExpectedError": "a validator has assigned the consumer key already: consumer key is already in use by a validator" + "ExpectError": false, + "ExpectedError": "" }, - "State": {} + "State": { + "densu": { + "ValBalances": null, + "ProposedConsumerChains": null, + "ValPowers": null, + "StakedTokens": null, + "Params": null, + "Rewards": null, + "ConsumerChains": null, + "AssignedKeys": { + "carol": "cosmosvalcons1kswr5sq599365kcjmhgufevfps9njf43e4lwdk" + }, + "ProviderKeys": { + "carol": "cosmosvalcons1ezyrq65s3gshhx5585w6mpusq3xsj3ayzf4uv6" + }, + "ConsumerPendingPacketQueueSize": null, + "RegisteredConsumerRewardDenoms": null, + "ClientsFrozenHeights": null, + "HasToValidate": null, + "Proposals": null + } + } }, { "ActionType": "main.AssignConsumerPubKeyAction", @@ -433,6 +486,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -470,6 +524,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "2": { "RawProposal": { @@ -529,6 +584,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -548,6 +604,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -601,6 +658,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -620,6 +678,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -639,6 +698,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -669,6 +729,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -688,6 +749,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -707,6 +769,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -737,6 +800,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -756,6 +820,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -775,6 +840,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -805,6 +871,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -824,6 +891,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -843,6 +911,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -873,6 +942,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -892,6 +962,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -911,6 +982,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -941,6 +1013,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -960,6 +1033,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -979,6 +1053,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1010,6 +1085,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -1029,6 +1105,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1048,6 +1125,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1078,6 +1156,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -1097,6 +1176,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1116,6 +1196,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1146,6 +1227,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -1165,6 +1247,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1184,6 +1267,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1212,6 +1296,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -1231,6 +1316,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1250,6 +1336,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1280,6 +1367,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -1299,6 +1387,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1318,6 +1407,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1348,6 +1438,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -1367,6 +1458,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1397,6 +1489,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -1416,6 +1509,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1444,6 +1538,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -1463,6 +1558,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1482,6 +1578,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1512,6 +1609,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -1531,6 +1629,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1550,6 +1649,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1580,6 +1680,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -1599,6 +1700,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1618,6 +1720,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1646,6 +1749,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -1665,6 +1769,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1684,6 +1789,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1714,6 +1820,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -1733,6 +1840,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1752,6 +1860,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1782,6 +1891,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -1801,6 +1911,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1820,6 +1931,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1848,6 +1960,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -1867,6 +1980,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1886,6 +2000,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1916,6 +2031,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -1935,6 +2051,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1954,6 +2071,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1984,6 +2102,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -2003,6 +2122,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -2022,6 +2142,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -2050,6 +2171,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -2069,6 +2191,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -2088,6 +2211,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -2118,6 +2242,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -2137,6 +2262,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -2156,6 +2282,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -2186,6 +2313,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -2205,6 +2333,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -2224,6 +2353,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -2252,6 +2382,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -2271,6 +2402,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -2290,6 +2422,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -2320,6 +2453,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -2339,6 +2473,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -2358,6 +2493,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -2388,6 +2524,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -2407,6 +2544,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -2426,6 +2564,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -2456,6 +2595,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "densu": { @@ -2475,6 +2615,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } diff --git a/tests/e2e/tracehandler_testdata/shorthappy.json b/tests/e2e/tracehandler_testdata/shorthappy.json index 607c1d6d7c..e029c5951b 100644 --- a/tests/e2e/tracehandler_testdata/shorthappy.json +++ b/tests/e2e/tracehandler_testdata/shorthappy.json @@ -41,6 +41,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -57,7 +58,8 @@ "InitialHeight": { "revision_height": 1 }, - "DistributionChannel": "" + "DistributionChannel": "", + "TopN": 100 }, "State": { "provi": { @@ -78,6 +80,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -123,6 +126,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -134,10 +138,31 @@ "Validator": "carol", "ConsumerPubkey": "{\"@type\":\"/cosmos.crypto.ed25519.PubKey\",\"key\":\"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=\"}", "ReconfigureNode": false, - "ExpectError": true, - "ExpectedError": "a validator has assigned the consumer key already: consumer key is already in use by a validator" + "ExpectError": false, + "ExpectedError": "" }, - "State": {} + "State": { + "consu": { + "ValBalances": null, + "ProposedConsumerChains": null, + "ValPowers": null, + "StakedTokens": null, + "Params": null, + "Rewards": null, + "ConsumerChains": null, + "AssignedKeys": { + "carol": "cosmosvalcons1kswr5sq599365kcjmhgufevfps9njf43e4lwdk" + }, + "ProviderKeys": { + "carol": "cosmosvalcons1ezyrq65s3gshhx5585w6mpusq3xsj3ayzf4uv6" + }, + "ConsumerPendingPacketQueueSize": null, + "RegisteredConsumerRewardDenoms": null, + "ClientsFrozenHeights": null, + "HasToValidate": null, + "Proposals": null + } + } }, { "ActionType": "main.AssignConsumerPubKeyAction", @@ -168,6 +193,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -205,6 +231,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -264,6 +291,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -283,6 +311,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -336,6 +365,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -355,6 +385,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -384,6 +415,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -414,6 +446,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -443,6 +476,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -473,6 +507,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -492,6 +527,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -522,6 +558,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -553,6 +590,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -572,6 +610,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -602,6 +641,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -630,6 +670,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -649,6 +690,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -678,6 +720,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -697,6 +740,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -727,6 +771,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -755,6 +800,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -774,6 +820,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -804,6 +851,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -832,6 +880,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -851,6 +900,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -881,6 +931,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -909,6 +960,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -928,6 +980,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -958,6 +1011,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -986,6 +1040,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1005,6 +1060,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1035,6 +1091,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -1054,6 +1111,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -1090,6 +1148,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "2": { "RawProposal": { @@ -1138,6 +1197,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "2": { "RawProposal": { @@ -1179,6 +1239,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "3": { "RawProposal": { @@ -1225,6 +1286,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "3": { "RawProposal": { diff --git a/tests/e2e/tracehandler_testdata/slashThrottle.json b/tests/e2e/tracehandler_testdata/slashThrottle.json index e99e7e2973..fb701a4dc1 100644 --- a/tests/e2e/tracehandler_testdata/slashThrottle.json +++ b/tests/e2e/tracehandler_testdata/slashThrottle.json @@ -41,6 +41,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -57,7 +58,8 @@ "InitialHeight": { "revision_height": 1 }, - "DistributionChannel": "" + "DistributionChannel": "", + "TopN": 100 }, "State": { "provi": { @@ -78,6 +80,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -123,6 +126,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -134,10 +138,31 @@ "Validator": "carol", "ConsumerPubkey": "{\"@type\":\"/cosmos.crypto.ed25519.PubKey\",\"key\":\"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=\"}", "ReconfigureNode": false, - "ExpectError": true, - "ExpectedError": "a validator has assigned the consumer key already: consumer key is already in use by a validator" + "ExpectError": false, + "ExpectedError": "" }, - "State": {} + "State": { + "consu": { + "ValBalances": null, + "ProposedConsumerChains": null, + "ValPowers": null, + "StakedTokens": null, + "Params": null, + "Rewards": null, + "ConsumerChains": null, + "AssignedKeys": { + "carol": "cosmosvalcons1kswr5sq599365kcjmhgufevfps9njf43e4lwdk" + }, + "ProviderKeys": { + "carol": "cosmosvalcons1ezyrq65s3gshhx5585w6mpusq3xsj3ayzf4uv6" + }, + "ConsumerPendingPacketQueueSize": null, + "RegisteredConsumerRewardDenoms": null, + "ClientsFrozenHeights": null, + "HasToValidate": null, + "Proposals": null + } + } }, { "ActionType": "main.AssignConsumerPubKeyAction", @@ -168,6 +193,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -205,6 +231,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "1": { "RawProposal": { @@ -264,6 +291,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -283,6 +311,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -336,6 +365,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -355,6 +385,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -384,6 +415,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -414,6 +446,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -443,6 +476,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -471,6 +505,7 @@ "ConsumerPendingPacketQueueSize": 1, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -490,6 +525,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -520,6 +556,7 @@ "ConsumerPendingPacketQueueSize": 0, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -539,6 +576,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -567,6 +605,7 @@ "ConsumerPendingPacketQueueSize": 1, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -586,6 +625,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -616,6 +656,7 @@ "ConsumerPendingPacketQueueSize": 1, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -635,6 +676,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -663,6 +705,7 @@ "ConsumerPendingPacketQueueSize": 1, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -682,6 +725,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -705,6 +749,7 @@ "ConsumerPendingPacketQueueSize": 1, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -724,6 +769,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -750,6 +796,7 @@ "ConsumerPendingPacketQueueSize": 0, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null }, "provi": { @@ -769,6 +816,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": null } } @@ -800,6 +848,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "2": { "RawProposal": { @@ -846,6 +895,7 @@ "ConsumerPendingPacketQueueSize": null, "RegisteredConsumerRewardDenoms": null, "ClientsFrozenHeights": null, + "HasToValidate": null, "Proposals": { "2": { "RawProposal": { diff --git a/tests/integration/common.go b/tests/integration/common.go index 18b657ae56..7c4e88c344 100644 --- a/tests/integration/common.go +++ b/tests/integration/common.go @@ -22,10 +22,10 @@ import ( abci "github.com/cometbft/cometbft/abci/types" tmtypes "github.com/cometbft/cometbft/types" - icstestingutils "github.com/cosmos/interchain-security/v4/testutil/ibc_testing" - testutil "github.com/cosmos/interchain-security/v4/testutil/integration" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" + testutil "github.com/cosmos/interchain-security/v5/testutil/integration" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // ChainType defines the type of chain (either provider or consumer) diff --git a/tests/integration/democracy.go b/tests/integration/democracy.go index bbada900a8..53333aba0e 100644 --- a/tests/integration/democracy.go +++ b/tests/integration/democracy.go @@ -14,9 +14,9 @@ import ( govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" - icstestingutils "github.com/cosmos/interchain-security/v4/testutil/ibc_testing" - testutil "github.com/cosmos/interchain-security/v4/testutil/integration" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" + testutil "github.com/cosmos/interchain-security/v5/testutil/integration" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" ) type ConsumerDemocracyTestSuite struct { diff --git a/tests/integration/distribution.go b/tests/integration/distribution.go index 1ea5bfcd00..edd7e3e0e1 100644 --- a/tests/integration/distribution.go +++ b/tests/integration/distribution.go @@ -4,15 +4,23 @@ import ( "strings" transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + + abci "github.com/cometbft/cometbft/abci/types" - icstestingutils "github.com/cosmos/interchain-security/v4/testutil/integration" - consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + icstestingutils "github.com/cosmos/interchain-security/v5/testutil/integration" + consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // This test is valid for minimal viable consumer chain @@ -42,6 +50,8 @@ func (s *CCVTestSuite) TestRewardsDistribution() { providerAccountKeeper := s.providerApp.GetTestAccountKeeper() consumerBankKeeper := s.consumerApp.GetTestBankKeeper() providerBankKeeper := s.providerApp.GetTestBankKeeper() + providerKeeper := s.providerApp.GetProviderKeeper() + providerDistributionKeeper := s.providerApp.GetTestDistributionKeeper() // send coins to the fee pool which is used for reward distribution consumerFeePoolAddr := consumerAccountKeeper.GetModuleAccount(s.consumerCtx(), authtypes.FeeCollectorName).GetAddress() @@ -70,7 +80,6 @@ func (s *CCVTestSuite) TestRewardsDistribution() { s.Require().Equal(providerExpectedRewards.AmountOf(sdk.DefaultBondDenom), providerTokens.AmountOf(sdk.DefaultBondDenom)) // send the reward to provider chain after 2 blocks - s.consumerChain.NextBlock() providerTokens = consumerBankKeeper.GetAllBalances(s.consumerCtx(), providerRedistributeAddr) s.Require().Equal(0, len(providerTokens)) @@ -82,36 +91,85 @@ func (s *CCVTestSuite) TestRewardsDistribution() { rewardPool := providerAccountKeeper.GetModuleAccount(s.providerCtx(), providertypes.ConsumerRewardsPool).GetAddress() rewardCoins := providerBankKeeper.GetAllBalances(s.providerCtx(), rewardPool) - ibcCoinIndex := -1 - for i, coin := range rewardCoins { + // Check that the reward pool contains a coin with an IBC denom + rewardsIBCdenom := "" + for _, coin := range rewardCoins { if strings.HasPrefix(coin.Denom, "ibc") { - ibcCoinIndex = i + rewardsIBCdenom = coin.Denom } } - - // Check that we found an ibc denom in the reward pool - s.Require().Greater(ibcCoinIndex, -1) + s.Require().NotZero(rewardsIBCdenom) // Check that the coins got into the ConsumerRewardsPool - s.Require().True(rewardCoins[ibcCoinIndex].Amount.Equal(providerExpectedRewards[0].Amount)) + providerExpRewardsAmount := providerExpectedRewards.AmountOf(sdk.DefaultBondDenom) + s.Require().Equal(rewardCoins.AmountOf(rewardsIBCdenom), providerExpRewardsAmount) // Advance a block and check that the coins are still in the ConsumerRewardsPool s.providerChain.NextBlock() rewardCoins = providerBankKeeper.GetAllBalances(s.providerCtx(), rewardPool) - s.Require().True(rewardCoins[ibcCoinIndex].Amount.Equal(providerExpectedRewards[0].Amount)) - - // Set the consumer reward denom. This would be done by a governance proposal in prod - s.providerApp.GetProviderKeeper().SetConsumerRewardDenom(s.providerCtx(), rewardCoins[ibcCoinIndex].Denom) + s.Require().Equal(rewardCoins.AmountOf(rewardsIBCdenom), providerExpRewardsAmount) + + // Set the consumer reward denom. This would be done by a governance proposal in prod. + providerKeeper.SetConsumerRewardDenom(s.providerCtx(), rewardsIBCdenom) + + // Refill the consumer fee pool + err = consumerBankKeeper.SendCoinsFromAccountToModule( + s.consumerCtx(), + s.consumerChain.SenderAccount.GetAddress(), + authtypes.FeeCollectorName, + fees, + ) + s.Require().NoError(err) - s.providerChain.NextBlock() + // Pass two blocks + s.consumerChain.NextBlock() + s.consumerChain.NextBlock() - // Check that the reward pool has no more coins because they were transferred to the fee pool + // Save the consumer validators total outstanding rewards on the provider + consumerValsOutstandingRewardsFunc := func(ctx sdk.Context) sdk.DecCoins { + totalRewards := sdk.DecCoins{} + for _, v := range providerKeeper.GetConsumerValSet(ctx, s.consumerChain.ChainID) { + val, ok := s.providerApp.GetTestStakingKeeper().GetValidatorByConsAddr(ctx, sdk.ConsAddress(v.ProviderConsAddr)) + s.Require().True(ok) + valReward := providerDistributionKeeper.GetValidatorOutstandingRewards(ctx, val.GetOperator()) + totalRewards = totalRewards.Add(valReward.Rewards...) + } + return totalRewards + } + consuValsRewards := consumerValsOutstandingRewardsFunc(s.providerCtx()) + + // Save community pool balance + communityPool := providerDistributionKeeper.GetFeePoolCommunityCoins(s.providerCtx()) + + // Transfer rewards from consumer to provider + relayAllCommittedPackets( + s, + s.consumerChain, + s.transferPath, + transfertypes.PortID, + s.transferPath.EndpointA.ChannelID, + 1, + ) + + // Check that the consumer rewards allocation are empty since relayAllCommittedPackets calls BeginBlockRD, + // which in turns calls AllocateTokens. + rewardsAlloc := providerKeeper.GetConsumerRewardsAllocation(s.providerCtx(), s.consumerChain.ChainID) + s.Require().Empty(rewardsAlloc.Rewards) + + // Check that the reward pool still holds the coins from the first transfer, + // which were never allocated since they were not whitelisted rewardCoins = providerBankKeeper.GetAllBalances(s.providerCtx(), rewardPool) - s.Require().Equal(0, len(rewardCoins)) + s.Require().Equal(rewardCoins.AmountOf(rewardsIBCdenom), providerExpRewardsAmount) - // check that the fee pool has the expected amount of coins - communityCoins := s.providerApp.GetTestDistributionKeeper().GetFeePoolCommunityCoins(s.providerCtx()) - s.Require().True(communityCoins[ibcCoinIndex].Amount.Equal(sdk.NewDecCoinFromCoin(providerExpectedRewards[0]).Amount)) + // Check that summing the rewards received by the consumer validators and the community pool + // is equal to the expected provider rewards + consuValsRewardsReceived := consumerValsOutstandingRewardsFunc(s.providerCtx()).Sub(consuValsRewards) + communityPoolDelta := providerDistributionKeeper.GetFeePoolCommunityCoins(s.providerCtx()).Sub(communityPool) + + s.Require().Equal( + sdk.NewDecFromInt(providerExpRewardsAmount), + consuValsRewardsReceived.AmountOf(rewardsIBCdenom).Add(communityPoolDelta.AmountOf(rewardsIBCdenom)), + ) } // TestSendRewardsRetries tests that failed reward transmissions are retried every BlocksPerDistributionTransmission blocks @@ -454,6 +512,396 @@ func (s *CCVTestSuite) TestSendRewardsToProvider() { } } +// TestIBCTransferMiddleware tests the logic of the IBC transfer OnRecvPacket callback +func (s *CCVTestSuite) TestIBCTransferMiddleware() { + var ( + data transfertypes.FungibleTokenPacketData + packet channeltypes.Packet + getIBCDenom func(string, string) string + ) + + testCases := []struct { + name string + setup func(sdk.Context, *providerkeeper.Keeper, icstestingutils.TestBankKeeper) + rewardsAllocated bool + expErr bool + }{ + { + "invalid IBC packet", + func(sdk.Context, *providerkeeper.Keeper, icstestingutils.TestBankKeeper) { + packet = channeltypes.Packet{} + }, + false, + true, + }, + { + "IBC packet sender isn't a consumer chain", + func(ctx sdk.Context, keeper *providerkeeper.Keeper, bankKeeper icstestingutils.TestBankKeeper) { + // make the sender consumer chain impossible to identify + packet.DestinationChannel = "CorruptedChannelId" + }, + false, + false, + }, + { + "IBC Transfer recipient is not the consumer rewards pool address", + func(ctx sdk.Context, keeper *providerkeeper.Keeper, bankKeeper icstestingutils.TestBankKeeper) { + data.Receiver = "cosmos149lw9fktlqfed3zt8ah48r5czmsug5s7kw77u9" // random acct address + packet.Data = data.GetBytes() + }, + false, + false, + }, + { + "IBC Transfer coin denom isn't registered", + func(ctx sdk.Context, keeper *providerkeeper.Keeper, bankKeeper icstestingutils.TestBankKeeper) {}, + false, + false, + }, + { + "successful token transfer to empty pool", + func(ctx sdk.Context, keeper *providerkeeper.Keeper, bankKeeper icstestingutils.TestBankKeeper) { + keeper.SetConsumerRewardDenom( + s.providerCtx(), + getIBCDenom(packet.DestinationPort, packet.DestinationChannel), + ) + }, + true, + false, + }, + { + "successful token transfer to filled pool", + func(ctx sdk.Context, keeper *providerkeeper.Keeper, bankKeeper icstestingutils.TestBankKeeper) { + keeper.SetConsumerRewardDenom( + ctx, + getIBCDenom(packet.DestinationPort, packet.DestinationChannel), + ) + + // fill consumer reward pool + bankKeeper.SendCoinsFromAccountToModule( + ctx, + s.providerChain.SenderAccount.GetAddress(), + providertypes.ConsumerRewardsPool, + sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(100_000))), + ) + // update consumer allocation + keeper.SetConsumerRewardsAllocation( + ctx, + s.consumerChain.ChainID, + providertypes.ConsumerRewardsAllocation{ + Rewards: sdk.NewDecCoins(sdk.NewDecCoin(sdk.DefaultBondDenom, math.NewInt(100_000))), + }, + ) + }, + true, + false, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + s.SetupTest() + s.SetupCCVChannel(s.path) + s.SetupTransferChannel() + + providerKeeper := s.providerApp.GetProviderKeeper() + bankKeeper := s.providerApp.GetTestBankKeeper() + amount := sdk.NewInt(100) + + data = transfertypes.NewFungibleTokenPacketData( // can be explicitly changed in setup + sdk.DefaultBondDenom, + amount.String(), + authtypes.NewModuleAddress(consumertypes.ConsumerToSendToProviderName).String(), + providerKeeper.GetConsumerRewardsPoolAddressStr(s.providerCtx()), + "", + ) + + packet = channeltypes.NewPacket( // can be explicitly changed in setup + data.GetBytes(), + uint64(1), + s.transferPath.EndpointA.ChannelConfig.PortID, + s.transferPath.EndpointA.ChannelID, + s.transferPath.EndpointB.ChannelConfig.PortID, + s.transferPath.EndpointB.ChannelID, + clienttypes.NewHeight(1, 100), + 0, + ) + + providerKeeper.SetConsumerRewardDenom(s.providerCtx(), + transfertypes.GetPrefixedDenom( + packet.DestinationPort, + packet.DestinationChannel, + sdk.DefaultBondDenom, + ), + ) + + getIBCDenom = func(dstPort, dstChannel string) string { + return transfertypes.ParseDenomTrace( + transfertypes.GetPrefixedDenom( + packet.DestinationPort, + packet.DestinationChannel, + sdk.DefaultBondDenom, + ), + ).IBCDenom() + } + + tc.setup(s.providerCtx(), &providerKeeper, bankKeeper) + + cbs, ok := s.providerChain.App.GetIBCKeeper().Router.GetRoute(transfertypes.ModuleName) + s.Require().True(ok) + + // save the IBC transfer rewards transferred + rewardsPoolBalance := bankKeeper.GetAllBalances(s.providerCtx(), sdk.MustAccAddressFromBech32(data.Receiver)) + + // save the consumer's rewards allocated + consumerRewardsAllocations := providerKeeper.GetConsumerRewardsAllocation(s.providerCtx(), s.consumerChain.ChainID) + + // execute middleware OnRecvPacket logic + ack := cbs.OnRecvPacket(s.providerCtx(), packet, sdk.AccAddress{}) + + // compute expected rewards with provider denom + expRewards := sdk.Coin{ + Amount: amount, + Denom: getIBCDenom(packet.DestinationPort, packet.DestinationChannel), + } + + // compute the balance and allocation difference + rewardsTransferred := bankKeeper.GetAllBalances(s.providerCtx(), sdk.MustAccAddressFromBech32(data.Receiver)). + Sub(rewardsPoolBalance...) + rewardsAllocated := providerKeeper.GetConsumerRewardsAllocation(s.providerCtx(), s.consumerChain.ChainID). + Rewards.Sub(consumerRewardsAllocations.Rewards) + + if !tc.expErr { + s.Require().True(ack.Success()) + // verify that the consumer rewards pool received the IBC coins + s.Require().Equal(rewardsTransferred, sdk.Coins{expRewards}) + + if tc.rewardsAllocated { + // check the data receiver address is set to the consumer rewards pool address + s.Require().Equal(data.GetReceiver(), providerKeeper.GetConsumerRewardsPoolAddressStr(s.providerCtx())) + + // verify that consumer rewards allocation is updated + s.Require().Equal(rewardsAllocated, sdk.NewDecCoinsFromCoins(expRewards)) + } else { + // verify that consumer rewards aren't allocated + s.Require().Empty(rewardsAllocated) + } + } else { + s.Require().False(ack.Success()) + } + }) + } +} + +// TestAllocateTokens is a happy-path test of the consumer rewards pool allocation +// to opted-in validators and the community pool +func (s *CCVTestSuite) TestAllocateTokens() { + // set up channel and delegate some tokens in order for validator set update to be sent to the consumer chain + s.SetupAllCCVChannels() + providerKeeper := s.providerApp.GetProviderKeeper() + bankKeeper := s.providerApp.GetTestBankKeeper() + distributionKeeper := s.providerApp.GetTestDistributionKeeper() + + totalRewards := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))} + + // fund consumer rewards pool + bankKeeper.SendCoinsFromAccountToModule( + s.providerCtx(), + s.providerChain.SenderAccount.GetAddress(), + providertypes.ConsumerRewardsPool, + totalRewards, + ) + + // Allocate rewards evenly between consumers + rewardsPerConsumer := totalRewards.QuoInt(math.NewInt(int64(len(s.consumerBundles)))) + for chainID := range s.consumerBundles { + // update consumer allocation + providerKeeper.SetConsumerRewardsAllocation( + s.providerCtx(), + chainID, + providertypes.ConsumerRewardsAllocation{ + Rewards: sdk.NewDecCoinsFromCoins(rewardsPerConsumer...), + }, + ) + } + + // Iterate over the validators and + // store their current voting power and outstanding rewards + lastValOutRewards := map[string]sdk.DecCoins{} + votes := []abci.VoteInfo{} + for _, val := range s.providerChain.Vals.Validators { + votes = append(votes, + abci.VoteInfo{ + Validator: abci.Validator{Address: val.Address, Power: val.VotingPower}, + SignedLastBlock: true, + }, + ) + + valRewards := distributionKeeper.GetValidatorOutstandingRewards(s.providerCtx(), sdk.ValAddress(val.Address)) + lastValOutRewards[sdk.ValAddress(val.Address).String()] = valRewards.Rewards + } + + // store community pool balance + lastCommPool := distributionKeeper.GetFeePoolCommunityCoins(s.providerCtx()) + + // execute BeginBlock to trigger the token allocation + providerKeeper.BeginBlockRD( + s.providerCtx(), + abci.RequestBeginBlock{ + LastCommitInfo: abci.CommitInfo{ + Votes: votes, + }, + }, + ) + + valNum := len(s.providerChain.Vals.Validators) + consuNum := len(s.consumerBundles) + + // compute the expected validators token allocation by subtracting the community tax + rewardsPerConsumerDec := sdk.NewDecCoinsFromCoins(rewardsPerConsumer...) + communityTax := distributionKeeper.GetCommunityTax(s.providerCtx()) + validatorsExpRewards := rewardsPerConsumerDec. + MulDecTruncate(math.LegacyOneDec().Sub(communityTax)). + // multiply by the number of consumers since all the validators opted in + MulDec(sdk.NewDec(int64(consuNum))) + perValExpReward := validatorsExpRewards.QuoDec(sdk.NewDec(int64(valNum))) + + // verify the validator tokens allocation + // note that the validators have the same voting power to keep things simple + for _, val := range s.providerChain.Vals.Validators { + valRewards := distributionKeeper.GetValidatorOutstandingRewards(s.providerCtx(), sdk.ValAddress(val.Address)) + s.Require().Equal( + valRewards.Rewards, + lastValOutRewards[sdk.ValAddress(val.Address).String()].Add(perValExpReward...), + ) + } + + commPoolExpRewards := sdk.NewDecCoinsFromCoins(totalRewards...).Sub(validatorsExpRewards) + currCommPool := distributionKeeper.GetFeePoolCommunityCoins(s.providerCtx()) + + s.Require().Equal(currCommPool, (lastCommPool.Add(commPoolExpRewards...))) +} + +// TestAllocateTokens is a unit-test for TransferConsumerRewardsToDistributionModule() +// but is written as an integration test to avoid excessive mocking. +func (s *CCVTestSuite) TransferConsumerRewardsToDistributionModule() { + testCases := []struct { + name string + rewardsPool sdk.Coins + rewardsAlloc sdk.DecCoins + expErr bool + }{ + { + "empty consumer rewards pool", + sdk.Coins{}, + sdk.DecCoins{}, + false, + }, + { + "empty consumer allocation", + sdk.Coins{ + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), + }, + sdk.DecCoins{}, + false, + }, + { + "equal consumer rewards pool and allocation", + sdk.Coins{ + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), + }, + sdk.DecCoins{ + sdk.NewDecCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), + }, + false, + }, + { + "less consumer rewards than allocation", + sdk.Coins{ + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(90)), + }, + sdk.DecCoins{ + sdk.NewDecCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), + }, + true, + }, + { + "remaining consumer rewards allocation", + sdk.Coins{ + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), + }, + sdk.DecCoins{ + sdk.DecCoin{ + Denom: sdk.DefaultBondDenom, + Amount: sdk.NewDecWithPrec(995, 1), + }, + }, + false, + }, + } + + providerKeeper := s.providerApp.GetProviderKeeper() + bankKeeper := s.providerApp.GetTestBankKeeper() + distributionKeeper := s.providerApp.GetTestDistributionKeeper() + + chainID := s.consumerChain.ChainID + + for _, tc := range testCases { + s.Run(tc.name, func() { + ctx, _ := s.providerCtx().CacheContext() + // fund consumer rewards pool + bankKeeper.SendCoinsFromAccountToModule( + ctx, + s.providerChain.SenderAccount.GetAddress(), + providertypes.ConsumerRewardsPool, + tc.rewardsPool, + ) + + // update consumer rewars allocation + providerKeeper.SetConsumerRewardsAllocation( + ctx, + chainID, + providertypes.ConsumerRewardsAllocation{ + Rewards: tc.rewardsAlloc, + }, + ) + + // store pool balance + oldPool := bankKeeper.GetAllBalances( + ctx, + distributionKeeper.GetDistributionAccount(ctx).GetAddress(), + ) + + // transfer consumer rewars to distribution module + coinsTransferred, err := providerKeeper.TransferConsumerRewardsToDistributionModule( + ctx, + chainID, + ) + if tc.expErr { + s.Require().Error(err) + return + } + + // check remaining consumer rewards allocation + expCoinTransferred, expRemaining := tc.rewardsAlloc.TruncateDecimal() + s.Require().Equal(expCoinTransferred, coinsTransferred) + + s.Require().Equal( + expRemaining, + providerKeeper.GetConsumerRewardsAllocation(ctx, chainID).Rewards, + ) + + // check updated consuemer rewards pool balance + newPool := bankKeeper.GetAllBalances( + ctx, + distributionKeeper.GetDistributionAccount(ctx).GetAddress(), + ) + + s.Require().Equal(newPool.Sub(oldPool...), coinsTransferred) + }) + } +} + // getEscrowBalance gets the current balances in the escrow account holding the transferred tokens to the provider func (s *CCVTestSuite) getEscrowBalance() sdk.Coins { consumerBankKeeper := s.consumerApp.GetTestBankKeeper() @@ -485,3 +933,221 @@ func (s *CCVTestSuite) prepareRewardDist() { blocksToGo := bpdt - blocksSinceLastTrans s.coordinator.CommitNBlocks(s.consumerChain, uint64(blocksToGo)) } + +func (s *CCVTestSuite) TestAllocateTokensToValidator() { + providerKeeper := s.providerApp.GetProviderKeeper() + distributionKeeper := s.providerApp.GetTestDistributionKeeper() + bankKeeper := s.providerApp.GetTestBankKeeper() + + chainID := s.consumerChain.ChainID + + testCases := []struct { + name string + consuValLen int + tokens sdk.DecCoins + rate sdk.Dec + expAllocated sdk.DecCoins + }{ + { + name: "tokens are empty", + tokens: sdk.DecCoins{}, + rate: sdk.ZeroDec(), + expAllocated: nil, + }, + { + name: "consumer valset is empty - total voting power is zero", + tokens: sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, math.NewInt(100_000))}, + rate: sdk.ZeroDec(), + expAllocated: nil, + }, + { + name: "expect all tokens to be allocated to a single validator", + consuValLen: 1, + tokens: sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, math.NewInt(999))}, + rate: sdk.NewDecWithPrec(5, 1), + expAllocated: sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, math.NewInt(999))}, + }, + { + name: "expect tokens to be allocated evenly between validators", + consuValLen: 2, + tokens: sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyNewDecFromIntWithPrec(math.NewInt(999), 2))}, + rate: sdk.OneDec(), + expAllocated: sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyNewDecFromIntWithPrec(math.NewInt(999), 2))}, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + ctx, _ := s.providerCtx().CacheContext() + + // change the consumer valset + consuVals := providerKeeper.GetConsumerValSet(ctx, chainID) + providerKeeper.DeleteConsumerValSet(ctx, chainID) + providerKeeper.SetConsumerValSet(ctx, chainID, consuVals[0:tc.consuValLen]) + consuVals = providerKeeper.GetConsumerValSet(ctx, chainID) + + // set the same consumer commission rate for all consumer validators + for _, v := range consuVals { + provAddr := providertypes.NewProviderConsAddress(sdk.ConsAddress(v.ProviderConsAddr)) + err := providerKeeper.SetConsumerCommissionRate( + ctx, + chainID, + provAddr, + tc.rate, + ) + s.Require().NoError(err) + } + + // allocate tokens + res := providerKeeper.AllocateTokensToConsumerValidators( + ctx, + chainID, + tc.tokens, + ) + + // check that the expected result is returned + s.Require().Equal(tc.expAllocated, res) + + if !tc.expAllocated.Empty() { + // rewards are expected to be allocated evenly between validators + rewardsPerVal := tc.expAllocated.QuoDec(sdk.NewDec(int64(len(consuVals)))) + + // check that the rewards are allocated to validators + for _, v := range consuVals { + valAddr := sdk.ValAddress(v.ProviderConsAddr) + rewards := s.providerApp.GetTestDistributionKeeper().GetValidatorOutstandingRewards( + ctx, + valAddr, + ) + s.Require().Equal(rewardsPerVal, rewards.Rewards) + + // send rewards to the distribution module + valRewardsTrunc, _ := rewards.Rewards.TruncateDecimal() + err := bankKeeper.SendCoinsFromAccountToModule( + ctx, + s.providerChain.SenderAccount.GetAddress(), + distrtypes.ModuleName, + valRewardsTrunc) + s.Require().NoError(err) + + // check that validators can withdraw their rewards + withdrawnCoins, err := distributionKeeper.WithdrawValidatorCommission( + ctx, + valAddr, + ) + s.Require().NoError(err) + + // check that the withdrawn coins is equal to the entire reward amount + // times the set consumer commission rate + commission := rewards.Rewards.MulDec(tc.rate) + c, _ := commission.TruncateDecimal() + s.Require().Equal(withdrawnCoins, c) + + // check that validators get rewards in their balance + s.Require().Equal(withdrawnCoins, bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddr))) + } + } else { + for _, v := range consuVals { + valAddr := sdk.ValAddress(v.ProviderConsAddr) + rewards := s.providerApp.GetTestDistributionKeeper().GetValidatorOutstandingRewards( + ctx, + valAddr, + ) + s.Require().Zero(rewards.Rewards) + } + } + }) + } +} + +// TestMultiConsumerRewardsDistribution tests the rewards distribution of multiple consumers chains +func (s *CCVTestSuite) TestMultiConsumerRewardsDistribution() { + s.SetupAllCCVChannels() + s.SetupAllTransferChannels() + + providerBankKeeper := s.providerApp.GetTestBankKeeper() + providerAccountKeeper := s.providerApp.GetTestAccountKeeper() + + // check that the reward provider pool is empty + rewardPool := providerAccountKeeper.GetModuleAccount(s.providerCtx(), providertypes.ConsumerRewardsPool).GetAddress() + rewardCoins := providerBankKeeper.GetAllBalances(s.providerCtx(), rewardPool) + s.Require().Empty(rewardCoins) + + totalConsumerRewards := sdk.Coins{} + + // Iterate over the consumers and perform the reward distribution + // to the provider + for chainID := range s.consumerBundles { + bundle := s.consumerBundles[chainID] + consumerKeeper := bundle.App.GetConsumerKeeper() + bankKeeper := bundle.App.GetTestBankKeeper() + accountKeeper := bundle.App.GetTestAccountKeeper() + + // set the consumer reward denom and the block per distribution params + params := consumerKeeper.GetConsumerParams(bundle.GetCtx()) + params.RewardDenoms = []string{sdk.DefaultBondDenom} + // set the reward distribution to be performed during the next block + params.BlocksPerDistributionTransmission = int64(1) + consumerKeeper.SetParams(bundle.GetCtx(), params) + + // transfer the consumer reward pool to the provider + var rewardsPerConsumer sdk.Coin + + // check the consumer pool balance + // Note that for a democracy consumer chain the pool may already be filled + if pool := bankKeeper.GetAllBalances( + bundle.GetCtx(), + accountKeeper.GetModuleAccount(bundle.GetCtx(), consumertypes.ConsumerToSendToProviderName).GetAddress(), + ); pool.Empty() { + // if pool is empty, fill it with some tokens + rewardsPerConsumer = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) + err := bankKeeper.SendCoinsFromAccountToModule( + bundle.GetCtx(), + bundle.Chain.SenderAccount.GetAddress(), + consumertypes.ConsumerToSendToProviderName, + sdk.NewCoins(rewardsPerConsumer), + ) + s.Require().NoError(err) + } else { + // execute the internal rewards distribution + // to save the pool's balance before + // it gets transferred to the provider in EndBlock + consumerKeeper.DistributeRewardsInternally(bundle.GetCtx()) + pool = bankKeeper.GetAllBalances( + bundle.GetCtx(), + accountKeeper.GetModuleAccount(bundle.GetCtx(), consumertypes.ConsumerToSendToProviderName).GetAddress(), + ) + s.Require().Len(pool, 1, "consumer reward pool cannot have mutiple token denoms") + rewardsPerConsumer = pool[0] + } + + // perform the reward transfer + bundle.Chain.NextBlock() + + // relay IBC transfer packet from consumer to provider + relayAllCommittedPackets( + s, + bundle.Chain, + bundle.TransferPath, + transfertypes.PortID, + bundle.TransferPath.EndpointA.ChannelID, + 1, + ) + + // construct the denom of the reward tokens for the provider + prefixedDenom := transfertypes.GetPrefixedDenom( + transfertypes.PortID, + bundle.TransferPath.EndpointB.ChannelID, + rewardsPerConsumer.Denom, + ) + provIBCDenom := transfertypes.ParseDenomTrace(prefixedDenom).IBCDenom() + + // sum the total rewards transferred to the provider + totalConsumerRewards = totalConsumerRewards. + Add(sdk.NewCoin(provIBCDenom, rewardsPerConsumer.Amount)) + } + + // Check that the provider receives the rewards of each consumer + rewardCoins = providerBankKeeper.GetAllBalances(s.providerCtx(), rewardPool) + s.Require().Equal(totalConsumerRewards, rewardCoins, totalConsumerRewards.String(), rewardCoins.String()) +} diff --git a/tests/integration/double_vote.go b/tests/integration/double_vote.go index c22ba3b063..c23d3feb06 100644 --- a/tests/integration/double_vote.go +++ b/tests/integration/double_vote.go @@ -7,8 +7,8 @@ import ( tmcrypto "github.com/cometbft/cometbft/crypto" tmtypes "github.com/cometbft/cometbft/types" - testutil "github.com/cosmos/interchain-security/v4/testutil/crypto" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + testutil "github.com/cosmos/interchain-security/v5/testutil/crypto" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) // TestHandleConsumerDoubleVoting verifies that handling a double voting evidence diff --git a/tests/integration/expired_client.go b/tests/integration/expired_client.go index a46df32d8e..d5405df1c2 100644 --- a/tests/integration/expired_client.go +++ b/tests/integration/expired_client.go @@ -14,7 +14,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // TestVSCPacketSendWithExpiredClient tests queueing of VSCPackets when the consumer client is expired. diff --git a/tests/integration/instance_test.go b/tests/integration/instance_test.go index d2896ad964..424c6c85f5 100644 --- a/tests/integration/instance_test.go +++ b/tests/integration/instance_test.go @@ -5,11 +5,11 @@ import ( "github.com/stretchr/testify/suite" - appConsumer "github.com/cosmos/interchain-security/v4/app/consumer" - appConsumerDemocracy "github.com/cosmos/interchain-security/v4/app/consumer-democracy" - appProvider "github.com/cosmos/interchain-security/v4/app/provider" - intg "github.com/cosmos/interchain-security/v4/tests/integration" - icstestingutils "github.com/cosmos/interchain-security/v4/testutil/ibc_testing" + appConsumer "github.com/cosmos/interchain-security/v5/app/consumer" + appConsumerDemocracy "github.com/cosmos/interchain-security/v5/app/consumer-democracy" + appProvider "github.com/cosmos/interchain-security/v5/app/provider" + intg "github.com/cosmos/interchain-security/v5/tests/integration" + icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" ) // This file can be used as an example integration testing instance for any provider/consumer applications. diff --git a/tests/integration/key_assignment.go b/tests/integration/key_assignment.go index ab6cb63146..ad7a1a142e 100644 --- a/tests/integration/key_assignment.go +++ b/tests/integration/key_assignment.go @@ -9,8 +9,8 @@ import ( tmencoding "github.com/cometbft/cometbft/crypto/encoding" tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) func (s *CCVTestSuite) TestKeyAssignment() { @@ -79,7 +79,7 @@ func (s *CCVTestSuite) TestKeyAssignment() { }, false, 2, }, { - "double same-key assignment in same block", func(pk *providerkeeper.Keeper) error { + "double same-key assignment in same block by different vals", func(pk *providerkeeper.Keeper) error { // establish CCV channel s.SetupCCVChannel(s.path) @@ -90,8 +90,9 @@ func (s *CCVTestSuite) TestKeyAssignment() { return err } - // same key assignment - err = pk.AssignConsumerKey(s.providerCtx(), s.consumerChain.ChainID, validator, consumerKey) + // same key assignment, but different validator + validator2, _ := generateNewConsumerKey(s, 1) + err = pk.AssignConsumerKey(s.providerCtx(), s.consumerChain.ChainID, validator2, consumerKey) if err != nil { return err } @@ -100,6 +101,28 @@ func (s *CCVTestSuite) TestKeyAssignment() { return nil }, true, 2, }, + { + "double same-key assignment in same block by same val", func(pk *providerkeeper.Keeper) error { + // establish CCV channel + s.SetupCCVChannel(s.path) + + // key assignment + validator, consumerKey := generateNewConsumerKey(s, 0) + err := pk.AssignConsumerKey(s.providerCtx(), s.consumerChain.ChainID, validator, consumerKey) + if err != nil { + return err + } + + // same key assignment, but different validator + err = pk.AssignConsumerKey(s.providerCtx(), s.consumerChain.ChainID, validator, consumerKey) + if err != nil { + return err + } + s.nextEpoch() + + return nil + }, false, 2, + }, { "double key assignment in same block", func(pk *providerkeeper.Keeper) error { // establish CCV channel @@ -124,7 +147,7 @@ func (s *CCVTestSuite) TestKeyAssignment() { }, false, 2, }, { - "double same-key assignment in different blocks", func(pk *providerkeeper.Keeper) error { + "double same-key assignment in different blocks by different vals", func(pk *providerkeeper.Keeper) error { // establish CCV channel s.SetupCCVChannel(s.path) @@ -137,7 +160,8 @@ func (s *CCVTestSuite) TestKeyAssignment() { s.nextEpoch() // same key assignment - err = pk.AssignConsumerKey(s.providerCtx(), s.consumerChain.ChainID, validator, consumerKey) + validator2, _ := generateNewConsumerKey(s, 1) + err = pk.AssignConsumerKey(s.providerCtx(), s.consumerChain.ChainID, validator2, consumerKey) if err != nil { return err } @@ -146,6 +170,29 @@ func (s *CCVTestSuite) TestKeyAssignment() { return nil }, true, 2, }, + { + "double same-key assignment in different blocks by same val", func(pk *providerkeeper.Keeper) error { + // establish CCV channel + s.SetupCCVChannel(s.path) + + // key assignment + validator, consumerKey := generateNewConsumerKey(s, 0) + err := pk.AssignConsumerKey(s.providerCtx(), s.consumerChain.ChainID, validator, consumerKey) + if err != nil { + return err + } + s.nextEpoch() + + // same key assignment + err = pk.AssignConsumerKey(s.providerCtx(), s.consumerChain.ChainID, validator, consumerKey) + if err != nil { + return err + } + s.nextEpoch() + + return nil + }, false, 2, + }, { "double key assignment in different blocks", func(pk *providerkeeper.Keeper) error { // establish CCV channel diff --git a/tests/integration/misbehaviour.go b/tests/integration/misbehaviour.go index acdcd0071c..cea0d15e5f 100644 --- a/tests/integration/misbehaviour.go +++ b/tests/integration/misbehaviour.go @@ -9,8 +9,8 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - testutil "github.com/cosmos/interchain-security/v4/testutil/crypto" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + testutil "github.com/cosmos/interchain-security/v5/testutil/crypto" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) // TestHandleConsumerMisbehaviour tests that handling a valid misbehaviour, diff --git a/tests/integration/normal_operations.go b/tests/integration/normal_operations.go index 81c742eeed..61f7507793 100644 --- a/tests/integration/normal_operations.go +++ b/tests/integration/normal_operations.go @@ -5,8 +5,8 @@ import ( tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // Tests the tracking of historical info in the context of new blocks being committed diff --git a/tests/integration/setup.go b/tests/integration/setup.go index e3bf2bff8e..a70dab3dec 100644 --- a/tests/integration/setup.go +++ b/tests/integration/setup.go @@ -20,12 +20,12 @@ import ( abci "github.com/cometbft/cometbft/abci/types" tmencoding "github.com/cometbft/cometbft/crypto/encoding" - icstestingutils "github.com/cosmos/interchain-security/v4/testutil/ibc_testing" - testutil "github.com/cosmos/interchain-security/v4/testutil/integration" - "github.com/cosmos/interchain-security/v4/testutil/simibc" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" + testutil "github.com/cosmos/interchain-security/v5/testutil/integration" + "github.com/cosmos/interchain-security/v5/testutil/simibc" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // Callback for instantiating a new coordinator with a provider test chains @@ -154,12 +154,16 @@ func (suite *CCVTestSuite) SetupTest() { preProposalKeyAssignment(suite, icstestingutils.FirstConsumerChainID) // start consumer chains - numConsumers := 5 suite.consumerBundles = make(map[string]*icstestingutils.ConsumerBundle) - for i := 0; i < numConsumers; i++ { + for i := 0; i < icstestingutils.NumConsumers; i++ { bundle := suite.setupConsumerCallback(&suite.Suite, suite.coordinator, i) suite.consumerBundles[bundle.Chain.ChainID] = bundle suite.registerPacketSniffer(bundle.Chain) + + // check that TopN is correctly set for the consumer + topN, found := providerKeeper.GetTopN(suite.providerCtx(), bundle.Chain.ChainID) + suite.Require().True(found) + suite.Require().Equal(bundle.TopN, topN) } // initialize each consumer chain with it's corresponding genesis state @@ -240,7 +244,6 @@ func initConsumerChain( ) s.Require().True(found, "provider endpoint clientID not found") bundle.Path.EndpointB.ClientID = providerEndpointClientID - // Set consumer endpoint's clientID consumerKeeper := bundle.GetKeeper() consumerEndpointClientID, found := consumerKeeper.GetProviderClientID(bundle.GetCtx()) @@ -320,34 +323,70 @@ func (suite *CCVTestSuite) ExecuteCCVChannelHandshake(path *ibctesting.Path) { // TODO: Make SetupTransferChannel functional for multiple consumers by pattern matching SetupCCVChannel. // See: https://github.com/cosmos/interchain-security/issues/506 +// SetupTransferChannel setup the transfer channel of the first consumer chain among multiple func (suite *CCVTestSuite) SetupTransferChannel() { - // transfer path will use the same connection as ccv path + suite.setupTransferChannel( + suite.transferPath, + suite.path, + suite.consumerApp.GetConsumerKeeper().GetDistributionTransmissionChannel( + suite.consumerChain.GetContext(), + ), + ) +} - suite.transferPath.EndpointA.ClientID = suite.path.EndpointA.ClientID - suite.transferPath.EndpointA.ConnectionID = suite.path.EndpointA.ConnectionID - suite.transferPath.EndpointB.ClientID = suite.path.EndpointB.ClientID - suite.transferPath.EndpointB.ConnectionID = suite.path.EndpointB.ConnectionID +func (suite *CCVTestSuite) setupTransferChannel( + transferPath *ibctesting.Path, + ccvPath *ibctesting.Path, + channelID string, +) { + // transfer path will use the same connection as ccv path + transferPath.EndpointA.ClientID = ccvPath.EndpointA.ClientID + transferPath.EndpointA.ConnectionID = ccvPath.EndpointA.ConnectionID + transferPath.EndpointB.ClientID = ccvPath.EndpointB.ClientID + transferPath.EndpointB.ConnectionID = ccvPath.EndpointB.ConnectionID // CCV channel handshake will automatically initiate transfer channel handshake on ACK // so transfer channel will be on stage INIT when CompleteSetupCCVChannel returns. - suite.transferPath.EndpointA.ChannelID = suite.consumerApp.GetConsumerKeeper().GetDistributionTransmissionChannel( - suite.consumerChain.GetContext()) + transferPath.EndpointA.ChannelID = channelID // Complete TRY, ACK, CONFIRM for transfer path - err := suite.transferPath.EndpointB.ChanOpenTry() + err := transferPath.EndpointB.ChanOpenTry() suite.Require().NoError(err) - err = suite.transferPath.EndpointA.ChanOpenAck() + err = transferPath.EndpointA.ChanOpenAck() suite.Require().NoError(err) - err = suite.transferPath.EndpointB.ChanOpenConfirm() + err = transferPath.EndpointB.ChanOpenConfirm() suite.Require().NoError(err) // ensure counterparty is up to date - err = suite.transferPath.EndpointA.UpdateClient() + err = transferPath.EndpointA.UpdateClient() suite.Require().NoError(err) } +// SetupAllTransferChannel setup all consumer chains transfer channel +func (suite *CCVTestSuite) SetupAllTransferChannels() { + // setup the first consumer transfer channel + suite.SetupTransferChannel() + + // setup all the remaining consumers transfer channels + for chainID := range suite.consumerBundles { + // skip fist consumer + if chainID == suite.consumerChain.ChainID { + continue + } + + // get the bundle for the chain ID + bundle := suite.consumerBundles[chainID] + // setup the transfer channel + suite.setupTransferChannel( + bundle.TransferPath, + bundle.Path, + bundle.App.GetConsumerKeeper().GetDistributionTransmissionChannel(bundle.GetCtx()), + ) + } +} + func (s CCVTestSuite) validateEndpointsClientConfig(consumerBundle icstestingutils.ConsumerBundle) { //nolint:govet // this is a test so we can copy locks consumerKeeper := consumerBundle.GetKeeper() providerStakingKeeper := s.providerApp.GetTestStakingKeeper() diff --git a/tests/integration/slashing.go b/tests/integration/slashing.go index e7f585f756..a9dcf223f2 100644 --- a/tests/integration/slashing.go +++ b/tests/integration/slashing.go @@ -18,9 +18,9 @@ import ( "github.com/cometbft/cometbft/crypto/ed25519" tmtypes "github.com/cometbft/cometbft/types" - keepertestutil "github.com/cosmos/interchain-security/v4/testutil/keeper" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + keepertestutil "github.com/cosmos/interchain-security/v5/testutil/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // TestRelayAndApplyDowntimePacket tests that downtime slash packets can be properly relayed @@ -406,6 +406,15 @@ func (suite *CCVTestSuite) TestOnRecvSlashPacketErrors() { // Check expected behavior for handling SlashPackets for downtime infractions slashPacketData.Infraction = stakingtypes.Infraction_INFRACTION_DOWNTIME + // Expect packet to be handled if the validator didn't opt in + ackResult, err = providerKeeper.OnRecvSlashPacket(ctx, packet, *slashPacketData) + suite.Require().NoError(err, "no error expected") + suite.Require().Equal(ccv.SlashPacketHandledResult, ackResult, "expected successful ack") + + providerKeeper.SetConsumerValidator(ctx, firstBundle.Chain.ChainID, providertypes.ConsumerValidator{ + ProviderConsAddr: validAddress, + }) + // Expect the packet to bounce if the slash meter is negative providerKeeper.SetSlashMeter(ctx, sdk.NewInt(-1)) ackResult, err = providerKeeper.OnRecvSlashPacket(ctx, packet, *slashPacketData) diff --git a/tests/integration/soft_opt_out.go b/tests/integration/soft_opt_out.go index bce2e1d77c..3799de79fc 100644 --- a/tests/integration/soft_opt_out.go +++ b/tests/integration/soft_opt_out.go @@ -10,8 +10,8 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - consumerKeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + consumerKeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // TestSoftOptOut tests the soft opt-out feature diff --git a/tests/integration/stop_consumer.go b/tests/integration/stop_consumer.go index 6e72679ccd..33f36fa186 100644 --- a/tests/integration/stop_consumer.go +++ b/tests/integration/stop_consumer.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // Tests the functionality of stopping a consumer chain at a higher level than unit tests diff --git a/tests/integration/throttle.go b/tests/integration/throttle.go index ff2d32dade..e9e013575c 100644 --- a/tests/integration/throttle.go +++ b/tests/integration/throttle.go @@ -10,10 +10,10 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - icstestingutils "github.com/cosmos/interchain-security/v4/testutil/ibc_testing" - "github.com/cosmos/interchain-security/v4/x/ccv/provider" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" + "github.com/cosmos/interchain-security/v5/x/ccv/provider" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) const fullSlashMeterString = "1.0" diff --git a/tests/integration/throttle_retry.go b/tests/integration/throttle_retry.go index 36acad3602..3d26847136 100644 --- a/tests/integration/throttle_retry.go +++ b/tests/integration/throttle_retry.go @@ -7,7 +7,7 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // TestSlashRetries tests the throttling v2 retry logic at an integration level. diff --git a/tests/integration/unbonding.go b/tests/integration/unbonding.go index 7f87516444..ca94fd3679 100644 --- a/tests/integration/unbonding.go +++ b/tests/integration/unbonding.go @@ -7,8 +7,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // TestUndelegationNormalOperation tests that undelegations complete after diff --git a/tests/integration/valset_update.go b/tests/integration/valset_update.go index eb0560a35e..113dcc01f4 100644 --- a/tests/integration/valset_update.go +++ b/tests/integration/valset_update.go @@ -10,7 +10,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // TestPacketRoundtrip tests a CCV packet roundtrip when tokens are bonded on provider diff --git a/tests/mbt/driver/core.go b/tests/mbt/driver/core.go index 3c985cb6fa..9235071d3e 100644 --- a/tests/mbt/driver/core.go +++ b/tests/mbt/driver/core.go @@ -24,14 +24,14 @@ import ( "github.com/cometbft/cometbft/proto/tendermint/crypto" cmttypes "github.com/cometbft/cometbft/types" - appConsumer "github.com/cosmos/interchain-security/v4/app/consumer" - appProvider "github.com/cosmos/interchain-security/v4/app/provider" - simibc "github.com/cosmos/interchain-security/v4/testutil/simibc" - consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - "github.com/cosmos/interchain-security/v4/x/ccv/types" + appConsumer "github.com/cosmos/interchain-security/v5/app/consumer" + appProvider "github.com/cosmos/interchain-security/v5/app/provider" + simibc "github.com/cosmos/interchain-security/v5/testutil/simibc" + consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // Define a new type for ChainIds to be more explicit diff --git a/tests/mbt/driver/mbt_test.go b/tests/mbt/driver/mbt_test.go index 9a82bd7b9b..e9a50f5c52 100644 --- a/tests/mbt/driver/mbt_test.go +++ b/tests/mbt/driver/mbt_test.go @@ -21,9 +21,9 @@ import ( tmencoding "github.com/cometbft/cometbft/crypto/encoding" cmttypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v4/testutil/integration" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/testutil/integration" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v5/x/ccv/types" ) const verbose = false diff --git a/tests/mbt/driver/setup.go b/tests/mbt/driver/setup.go index f6f68f14c5..d30b85bf30 100644 --- a/tests/mbt/driver/setup.go +++ b/tests/mbt/driver/setup.go @@ -30,11 +30,12 @@ import ( cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" cmttypes "github.com/cometbft/cometbft/types" - icstestingutils "github.com/cosmos/interchain-security/v4/testutil/ibc_testing" - "github.com/cosmos/interchain-security/v4/testutil/integration" - simibc "github.com/cosmos/interchain-security/v4/testutil/simibc" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" + "github.com/cosmos/interchain-security/v5/testutil/integration" + simibc "github.com/cosmos/interchain-security/v5/testutil/simibc" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) const ( @@ -377,7 +378,8 @@ func (s *Driver) ConfigureNewPath(consumerChain, providerChain *ibctesting.TestC stakingValidators = append(stakingValidators, v) } - nextValidators := s.providerKeeper().ComputeNextEpochConsumerValSet(s.providerCtx(), string(consumerChainId), stakingValidators) + considerAll := func(providerAddr types.ProviderConsAddress) bool { return true } + nextValidators := s.providerKeeper().FilterValidators(s.providerCtx(), string(consumerChainId), stakingValidators, considerAll) s.providerKeeper().SetConsumerValSet(s.providerCtx(), string(consumerChainId), nextValidators) err = s.providerKeeper().SetConsumerGenesis( @@ -386,6 +388,9 @@ func (s *Driver) ConfigureNewPath(consumerChain, providerChain *ibctesting.TestC consumerGenesisForProvider) require.NoError(s.t, err, "Error setting consumer genesis on provider for chain %v", consumerChain.ChainID) + // set the top N percentage to 100 to simulate a full consumer + s.providerKeeper().SetTopN(providerChain.GetContext(), consumerChain.ChainID, 100) + // Client ID is set in InitGenesis and we treat it as a black box. So // must query it to use it with the endpoint. clientID, _ := s.consumerKeeper(consumerChainId).GetProviderClientID(s.ctx(consumerChainId)) diff --git a/tests/mbt/model/README.md b/tests/mbt/model/README.md index b8ce579440..f4add0e213 100644 --- a/tests/mbt/model/README.md +++ b/tests/mbt/model/README.md @@ -32,6 +32,8 @@ All the logic in EndBlock/BeginBlock happens here, like updating the validator s * `DeliverVscPacket(receiver: Chain)`: Delivers a pending VSCPacket from the provider to the consumer `receiver`. * `DeliverPacketToProvider(sender: Chain)`: Delivers a pending packet from the consumer `sender` to the provider. * `KeyAssignment(chain: Chain, validator: Node, consumerAddr: ConsumerAddr)` (only when running with `--step stepKeyAssignment`): Assigns the `consumerAddr` to the `validator` on the `chain`. Note that we use "key" and "consumerAddr" pretty much interchangeably, as the model makes no differentiation between private keys, public keys, addresses, etc, as it doesn't model the cryptography. +* `OptIn(chain: Chain, validator: Node)` (only when running with `--step stepBoundedDriftKeyAndPSS` on `ccv_boundeddrift.qnt`): The `validator` opts in on the `chain`. +* `OptOut(chain: Chain, validator: Node)` (only when running with `--step stepBoundedDriftKeyAndPSS` on `ccv_boundeddrift.qnt`): The `validator` opts out on the `chain`. ### State machines @@ -51,6 +53,12 @@ To run with key assignment, specify the step flag: `--step stepKeyAssignment`. KeyAssignment also needs some different invariants, see below. +#### Partial Set Security + +To run with Partial Set Security, specify the step flag `--step stepBoundedDriftKeyAndPSS`. +This runs both PSS and Key Assignment. +It also requires running with `ccv_boundeddrift.qnt`, see below. + #### ccv_boundeddrift.qnt This state machine layer is more restricted to generate more interesting traces: * It never allows consumer chains to drift more than `MaxDrift` time apart from each other. @@ -75,10 +83,8 @@ traces are not very useful for testing. To run unit tests, run ``` -quint test ccv_test.qnt -``` -and -``` +quint test ccv_test.qnt; +quint test ccv_pss_test.qnt; quint test ccv_model.qnt ``` @@ -132,4 +138,8 @@ The available sanity checks are: - CanSendVscMaturedPackets - CanReceiveMaturations - CanAssignConsumerKey (only with `--step stepKeyAssignment`) -- CanHaveConsumerAddresses (only with `--step stepKeyAssignment`) \ No newline at end of file +- CanHaveConsumerAddresses (only with `--step stepKeyAssignment`) +- CanOptIn (only with `--step stepBoundedDriftKeyAndPSS` on `ccv_boundeddrift.qnt`) +- CanOptOut (only with `--step stepBoundedDriftKeyAndPSS` on `ccv_boundeddrift.qnt`) +- CanFailOptOut (only with `--step stepBoundedDriftKeyAndPSS` on `ccv_boundeddrift.qnt`) +- CanHaveOptIn (only with `--step stepBoundedDriftKeyAndPSS` on `ccv_boundeddrift.qnt`) diff --git a/tests/mbt/model/ccv.qnt b/tests/mbt/model/ccv.qnt index 87ddc91ea2..80aed91ec2 100644 --- a/tests/mbt/model/ccv.qnt +++ b/tests/mbt/model/ccv.qnt @@ -908,14 +908,18 @@ module ccv { // (in general, the validator is a consumer address and could be an assigned one) val slashFactor = DowntimeSlashPercentage + + val curValPowerIsZero = currentState.providerState.chainState.currentValidatorPowers.get(providerVal) == 0 - val newProviderState = currentState.providerState - .jailUntil(providerVal, jailEndTime) + val newProviderState = if (not(curValPowerIsZero)) + currentState.providerState + .jailUntil(providerVal, jailEndTime) else + currentState.providerState // validators with zero power are not jailed // // slashing is currently not enabled in ICS // .slash(packet.validator, packet.valPower, slashFactor) // if the validator did not have voting power of 0 or was already jailed before, the validator set will change due to the slash - val valSetChanged = currentState.providerState.chainState.currentValidatorPowers.get(providerVal) != 0 + val valSetChanged = not(curValPowerIsZero) // add the consumer address to the list of acknowledged slashes // for the sender chain of this slash packet and indicate that we need to send a packet at endblock diff --git a/tests/mbt/model/ccv_model.qnt b/tests/mbt/model/ccv_model.qnt index e2b812e212..312f6a5bdb 100644 --- a/tests/mbt/model/ccv_model.qnt +++ b/tests/mbt/model/ccv_model.qnt @@ -216,11 +216,14 @@ module ccv_model { // stepCommon is the core functionality of steps that does not have anything to do with time. action stepCommon = any { - nondet node = oneOf(nonJailedNodes(currentState.providerState)) - // very restricted set of voting powers. exact values are not important, - // and this keeps the state space smaller. - nondet newVotingPower = oneOf(Set(-50, 50)) - VotingPowerChange(node, newVotingPower), + all { + currentState.providerState.validatorsWithPower().size() > 0, + nondet node = oneOf(currentState.providerState.validatorsWithPower()) + // very restricted set of voting powers. exact values are not important, + // and this keeps the state space smaller. + nondet newVotingPower = oneOf(Set(-50, 50)) + VotingPowerChange(node, newVotingPower), + }, // try to send a packet. we could filter by chains that can actually send, // but it's probably not much faster than just trying and failing. @@ -749,7 +752,8 @@ module ccv_model { action nondetKeyAssignment = all { runningConsumers.size() > 0, - nondet node = oneOf(nodes) + currentState.providerState.validatorsWithPower().size() > 0, + nondet node = oneOf(currentState.providerState.validatorsWithPower()) nondet consumerAddr = oneOf(consumerAddresses) nondet consumer = oneOf(runningConsumers) KeyAssignment(consumer, node, consumerAddr), diff --git a/tests/mbt/model/ccv_pss.qnt b/tests/mbt/model/ccv_pss.qnt new file mode 100644 index 0000000000..26aee28d56 --- /dev/null +++ b/tests/mbt/model/ccv_pss.qnt @@ -0,0 +1,157 @@ +// This module contains logic for PSS (Partial Set Security). +// PSS is a variant/extension of CCV that +// allows for only a subset of the validator set +// to secure a consumer chain. +// Not all logic related to PSS is inside this module, as some logic is +// too tightly coupled with the core CCV logic, +// which is instead found in ccv.qnt +module ccv_pss { + import ccv_types.* from "./ccv" + import extraSpells.* from "./libraries/extraSpells" + import ccv_utils.* from "./ccv_utils" + + // Given a base validator set, an N for a top N chain, and a set of validators that have opted in to the chain, + // returns the validator set that should be sent to the chain. + // Assumes that the value for N is valid. + pure def GetPSSValidatorSet(providerState: ProviderState, origValSet: ValidatorSet, consumer: Chain): ValidatorSet = { + pure val optedInVals = providerState.optedInVals.getOrElse(consumer, Set()) + GetPSSValidatorSet_helper(origValSet, optedInVals) + } + + pure def GetPSSValidatorSet_helper(origValSet: ValidatorSet, optedInVals: Set[Node]): ValidatorSet = { + origValSet.mapFilter(v => optedInVals.contains(v)) + } + + // Given a validator set and N, returns the top N% of validators by power. + // Note that in the edge case of multiple validators having the same power, + // this will always include all validators with the same power as the lowest top N validator. + pure def GetTopNVals(origValSet: ValidatorSet, N: int): Set[Node] = { + // == sort validators by power == + // define a comparator that compares validators by power + pure def powerCompare(a: Node, b: Node): Ordering = { + pure val powA = origValSet.get(a) + pure val powB = origValSet.get(b) + intCompare(powB, powA) + } + // get a sorted list of validators by power + pure val sortedVals = origValSet.keys().toSortedList(powerCompare) + + // == compute the threshold of how much power the top N have == + pure val totalPower = origValSet.mapValuesSum() + pure val topNPower = totalPower * N / 100 + + // == construct the validator set by going through the sorted vals == + pure val res = sortedVals.foldl( + // accumulator carries 4 values: + // * set of vals in top N (starts with empty set) + // * total power added so far (starts with 0) + // * whether we should add the next validator if it has the same power as the previous one, + // regardless of total power (starts with false) + // * the power of the last validator added (starts with 0) + (Set(), 0, false, 0), + (acc, validator) => + pure val curValSet = acc._1 + pure val accPower = acc._2 + pure val shouldAddSamePow = acc._3 + pure val lastPow = acc._4 + + pure val validatorPower = origValSet.get(validator) + if (validatorPower == lastPow and shouldAddSamePow) { + // we should add the validator because it has the same power as the previous one, + // and we add regardless of total power because we need to include all + // vals with the same power if we include one of them + pure val newAccPower = accPower + validatorPower + (curValSet.union(Set(validator)), newAccPower, true, validatorPower) + } else if (validatorPower > 0 and accPower < topNPower) { + // if we don't have enough power yet, add the validator to the set + pure val newAccPower = accPower + validatorPower + (curValSet.union(Set(validator)), newAccPower, true, validatorPower) + } else { + // if we have enough power and we also are done adding + // all validators with the same power as the lowest top N validator, + // don't add them + (curValSet, accPower, false, 0) + } + ) + res._1 + } + + // Opts a validator in for a consumer chain the provider. + // Possible before the consumer chain starts running, + // and will then be applied when the consumer chain starts running. + pure def OptIn(currentState: ProtocolState, consumer: Chain, validator: Node): Result = { + pure val optedInVals = currentState.providerState.optedInVals.get(consumer) + pure val newOptedInVals = optedInVals.union(Set(validator)) + Ok({ + ...currentState, + providerState: { + ...currentState.providerState, + optedInVals: currentState.providerState.optedInVals.put(consumer, newOptedInVals) + } + }) + } + + // Returns true if the given validator is in the top N for the given consumer chain, + // and false otherwise. + pure def IsTopN(currentState: ProtocolState, validator: Node, consumer: Chain): bool = { + val proviValSet = currentState.providerState.chainState.votingPowerHistory.head() + val N = currentState.providerState.topNByConsumer.get(consumer) + + val topNValSet = GetTopNVals(proviValSet, N) + + topNValSet.contains(validator) + } + + // Returns true if the given validator has opted in to the given consumer chain, + pure def IsOptedIn(currentState: ProtocolState, validator: Node, consumer: Chain): bool = { + currentState.providerState.optedInVals.getOrElse(consumer, Set()).contains(validator) + } + + // Opts a validator out. Safe to call before the consumer chain even runs. + // Will not stop the validator set from being forced to validate when in the top N. + // Validators that are in the top N will not be able to opt out, and + // an error will be returned. + // Similarly, if the validator is not opted in, an error will be returned. + pure def OptOut(currentState: ProtocolState, consumer: Chain, validator: Node): Result = { + if (currentState.IsTopN(validator, consumer)) { + Err("Cannot opt out a validator that is in the top N") + } else if (not(currentState.IsOptedIn(validator, consumer))) { + Err("Cannot opt out a validator that is not opted in") + } else { + pure val optedInVals = currentState.providerState.optedInVals.get(consumer) + pure val newOptedInVals = optedInVals.exclude(Set(validator)) + Ok({ + ...currentState, + providerState: { + ...currentState.providerState, + optedInVals: currentState.providerState.optedInVals.put(consumer, newOptedInVals) + } + }) + } + } + + // Runs the PSS logic that needs to run on endblock. + // Concretely, this will forcefully opt in all validators that are in the top N + // for each chain. + pure def endBlockPSS(providerState: ProviderState): ProviderState = { + val runningConsumers = providerState.getRunningConsumers() + runningConsumers.fold( + providerState, + (acc, consumer) => endBlockPSS_helper(acc, consumer) + ) + } + + // Runs the PSS logic for a single consumer. + // Should only be run for running chains. + pure def endBlockPSS_helper(providerState: ProviderState, consumer: Chain): ProviderState = { + val proviValSet = providerState.chainState.currentValidatorPowers + val topNVals = GetTopNVals(proviValSet, providerState.topNByConsumer.get(consumer)) + val prevOptedInVals = providerState.optedInVals.getOrElse(consumer, Set()) + // opt in all the top N validators, i.e. union the top N vals with the previous opted in vals + val newOptedInVals = providerState.optedInVals.put(consumer, prevOptedInVals.union(topNVals)) + { + ...providerState, + optedInVals: newOptedInVals + } + } +} \ No newline at end of file diff --git a/tests/mbt/model/ccv_pss_model.qnt b/tests/mbt/model/ccv_pss_model.qnt new file mode 100644 index 0000000000..9e60e241bc --- /dev/null +++ b/tests/mbt/model/ccv_pss_model.qnt @@ -0,0 +1,113 @@ +module ccv_pss_model { + import ccv_types.* from "./ccv" + import ccv_model.* from "./ccv_model" + import ccv_pss.* from "./ccv_pss" + import extraSpells.* from "./libraries/extraSpells" + + action StepOptIn(): bool = { + all { + runningConsumers.size() > 0, + nondet consumer = oneOf(runningConsumers) + nondet validator = oneOf(nodes) + OptIn_Deterministic(consumer, validator) + } + } + + action OptIn_Deterministic(consumer: Chain, validator: Node): bool = { + val res = OptIn(currentState, consumer, validator) + all { + currentState' = res.newState, + trace' = trace.append( + { + ...emptyAction, + kind: "OptIn", + consumerChain: consumer, + validator: validator + } + ), + params' = params, + } + } + + action StepOptOut(): bool = { + all { + runningConsumers.size() > 0, + nondet consumer = oneOf(runningConsumers) + nondet validator = oneOf(nodes) + OptOut_Deterministic(consumer, validator) + } + } + + action OptOut_Deterministic(consumer: Chain, validator: Node): bool = { + val res = OptOut(currentState, consumer, validator) + all { + // if we expect an error, this should be a noop + currentState' = if (res.error == "") res.newState else currentState, + trace' = trace.append( + { + ...emptyAction, + kind: "OptOut", + consumerChain: consumer, + validator: validator, + expectedError: res.error + } + ), + params' = params, + } + } + + // Different sets of possible values for the topN parameter. + val allFullConsumers: Set[int] = Set(100) + val allOptIn: Set[int] = Set(0) + // only choose a few values for top N here to not make the "edge cases" of 0 and 100 too unlikely + val variousPossibleTopN: Set[int] = Set(50, 70, 80, 90, 100) + + // INVARIANTS + + // For a consumer chain with a given top N value, + // the total VP on the consumer is at least N% of the total VP of some historical val set on the provider. + val AtLeastTopNPower: bool = + runningConsumers.forall(consumer => { + val topN = currentState.providerState.topNByConsumer.get(consumer) + val totalPowerConsu = currentState.consumerStates.get(consumer).chainState.currentValidatorPowers.mapValuesSum() + currentState.providerState.chainState.votingPowerHistory.toSet().exists( + valSet => { + val totalPowerProvi = valSet.mapValuesSum() + + totalPowerConsu >= totalPowerProvi * topN / 100 + } + ) + }) + + // SANITY CHECKS + + val CanOptIn = { + not( + trace[length(trace)-1].kind == "OptIn" + and + trace[length(trace)-1].expectedError == "" + ) + } + + val CanOptOut = { + not( + trace[length(trace)-1].kind == "OptOut" + and + trace[length(trace)-1].expectedError == "" + ) + } + + val CanFailOptOut = { + not( + trace[length(trace)-1].kind == "OptOut" + and + trace[length(trace)-1].expectedError != "" + ) + } + + val CanHaveOptIn = { + currentState.providerState.topNByConsumer.keys().exists(consumer => { + currentState.providerState.topNByConsumer.get(consumer) != 100 + }) + } +} \ No newline at end of file diff --git a/tests/mbt/model/ccv_pss_test.qnt b/tests/mbt/model/ccv_pss_test.qnt new file mode 100644 index 0000000000..61a5420150 --- /dev/null +++ b/tests/mbt/model/ccv_pss_test.qnt @@ -0,0 +1,61 @@ +// This module contains logic for PSS (Partial Set Security). +// PSS is a variant/extension of CCV that +// allows for only a subset of the validator set +// to secure a consumer chain. +// Not all logic related to PSS is inside this module, as some logic is +// too tightly coupled with the core CCV logic, +// which is instead found in ccv.qnt +module ccv_pss_test { + import ccv_types.* from "./ccv" + import extraSpells.* from "./libraries/extraSpells" + import ccv_utils.* from "./ccv_utils" + import ccv_pss.* from "./ccv_pss" + import ccv.* from "./ccv" + + run TopN1Test = + val valSet = + Map("d" -> 25, "c1" -> 15, "c" -> 15, "b2" -> 10, "b1" -> 10, "b" -> 10, "a2" -> 5, "a1" -> 5, "a" -> 5) + // total power: 5*3 + 10*3 + 15*2 + 25 = 100 + all + { + assert(GetTopNVals(valSet, 0) == Set()), + assert(GetTopNVals(valSet, 1) == Set("d")), + assert(GetTopNVals(valSet, 10) == Set("d")), + assert(GetTopNVals(valSet, 25) == Set("d")), + // if one validator with a power is included, all validators with that power need to be included + assert(GetTopNVals(valSet, 26) == Set("d", "c1", "c")), + assert(GetTopNVals(valSet, 45) == Set("d", "c1", "c")), + assert(GetTopNVals(valSet, 55) == Set("d", "c1", "c")), + assert(GetTopNVals(valSet, 56) == Set("d", "c1", "c", "b2", "b1", "b")), + assert(GetTopNVals(valSet, 85) == Set("d", "c1", "c", "b2", "b1", "b")), + assert(GetTopNVals(valSet, 86) == valSet.keys()), + assert(GetTopNVals(valSet, 95) == valSet.keys()), + assert(GetTopNVals(valSet, 100) == valSet.keys()), + } + + val providerState = GetEmptyProviderState().with( + "chainState", GetEmptyChainState().with( + "currentValidatorPowers", Map( + "a" -> 5, + "a1" -> 5, + "a2" -> 5, + "b" -> 10, + "b1" -> 10, + "b2" -> 10, + "c" -> 15, + "c1" -> 15, + "d" -> 25 + ) + ) + ).with( + "consumerStatus", Map( + "consumer1" -> "running" + ) + ).with( + "topNByConsumer", Map( + "consumer1" -> 80 + ) + ) + run TopN2Test = + true +} \ No newline at end of file diff --git a/tests/mbt/model/ccv_utils.qnt b/tests/mbt/model/ccv_utils.qnt index ffb22e0dda..6c0f18b44c 100644 --- a/tests/mbt/model/ccv_utils.qnt +++ b/tests/mbt/model/ccv_utils.qnt @@ -531,6 +531,11 @@ module ccv_utils { ) } + // Returns the set of all nodes that are neither jailed nor have 0 power on the provider. + pure def validatorsWithPower(providerState: ProviderState): Set[Node] = { + nonJailedNodes(providerState).filter(node => providerState.chainState.currentValidatorPowers.get(node) != 0) + } + pure def IsEmptyValSet(valSet: ValidatorSet): bool = { valSet.keys().filter( node => valSet.get(node) > 0 diff --git a/testutil/crypto/crypto.go b/testutil/crypto/crypto.go index a9c5341947..df37e99eb5 100644 --- a/testutil/crypto/crypto.go +++ b/testutil/crypto/crypto.go @@ -15,7 +15,7 @@ import ( tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" tmtypes "github.com/cometbft/cometbft/types" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) // CryptoIdentity is a test helper for generating keys and addresses of diff --git a/testutil/ibc_testing/generic_setup.go b/testutil/ibc_testing/generic_setup.go index 704657506f..9d5ec02a71 100644 --- a/testutil/ibc_testing/generic_setup.go +++ b/testutil/ibc_testing/generic_setup.go @@ -16,9 +16,10 @@ import ( tmencoding "github.com/cometbft/cometbft/crypto/encoding" tmtypes "github.com/cometbft/cometbft/types" - testutil "github.com/cosmos/interchain-security/v4/testutil/integration" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + testutil "github.com/cosmos/interchain-security/v5/testutil/integration" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) type ( @@ -30,10 +31,16 @@ type ( // and/or democracy consumer app.go implementation. You should not need to modify or replicate this file // to run integration tests against your app.go implementations! +const ( + // Default number of consumer chains + NumConsumers = 5 +) + var ( FirstConsumerChainID string provChainID string democConsumerChainID string + consumerTopNParams [NumConsumers]uint32 ) func init() { @@ -42,6 +49,9 @@ func init() { FirstConsumerChainID = ibctesting.GetChainID(2) provChainID = ibctesting.GetChainID(1) democConsumerChainID = ibctesting.GetChainID(5000) + // TopN parameter values per consumer chain initiated + // sorted in ascending order i.e. testchain2, testchain3, ..., testchain6 + consumerTopNParams = [NumConsumers]uint32{100, 100, 100, 100, 100} } // ConsumerBundle serves as a way to store useful in-mem consumer app chain state @@ -51,6 +61,7 @@ type ConsumerBundle struct { App testutil.ConsumerApp Path *ibctesting.Path TransferPath *ibctesting.Path + TopN uint32 } // GetCtx returns the context for the ConsumerBundle @@ -116,6 +127,9 @@ func AddConsumer[Tp testutil.ProviderApp, Tc testutil.ConsumerApp]( index int, appIniter ValSetAppIniter, ) *ConsumerBundle { + // check index isn't bigger that the number of consumers + s.Require().LessOrEqual(index, NumConsumers) + // consumer chain ID chainID := ibctesting.GetChainID(index + 2) @@ -126,6 +140,14 @@ func AddConsumer[Tp testutil.ProviderApp, Tc testutil.ConsumerApp]( prop := testkeeper.GetTestConsumerAdditionProp() prop.ChainId = chainID + prop.Top_N = consumerTopNParams[index] // isn't used in CreateConsumerClient + + // opt-in all validators + for _, v := range providerApp.GetTestStakingKeeper().GetLastValidators(providerChain.GetContext()) { + consAddr, _ := v.GetConsAddr() + providerKeeper.SetOptedIn(providerChain.GetContext(), chainID, providertypes.NewProviderConsAddress(consAddr)) + } + // NOTE: the initial height passed to CreateConsumerClient // must be the height on the consumer when InitGenesis is called prop.InitialHeight = clienttypes.Height{RevisionNumber: 0, RevisionHeight: 3} @@ -135,6 +157,10 @@ func AddConsumer[Tp testutil.ProviderApp, Tc testutil.ConsumerApp]( ) s.Require().NoError(err) + // set the consumer TopN here since the test suite setup only used the consumer addition prop + // to create the consumer genesis, see BeginBlockInit in /x/ccv/provider/keeper/proposal.go. + providerKeeper.SetTopN(providerChain.GetContext(), chainID, prop.Top_N) + // commit the state on the provider chain coordinator.CommitBlock(providerChain) @@ -174,5 +200,6 @@ func AddConsumer[Tp testutil.ProviderApp, Tc testutil.ConsumerApp]( return &ConsumerBundle{ Chain: testChain, App: consumerToReturn, + TopN: prop.Top_N, } } diff --git a/testutil/ibc_testing/specific_setup.go b/testutil/ibc_testing/specific_setup.go index 2571346ca6..81788e10bc 100644 --- a/testutil/ibc_testing/specific_setup.go +++ b/testutil/ibc_testing/specific_setup.go @@ -17,11 +17,11 @@ import ( "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/log" - appConsumer "github.com/cosmos/interchain-security/v4/app/consumer" - appConsumerDemocracy "github.com/cosmos/interchain-security/v4/app/consumer-democracy" - appProvider "github.com/cosmos/interchain-security/v4/app/provider" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + appConsumer "github.com/cosmos/interchain-security/v5/app/consumer" + appConsumerDemocracy "github.com/cosmos/interchain-security/v5/app/consumer-democracy" + appProvider "github.com/cosmos/interchain-security/v5/app/provider" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) var ( diff --git a/testutil/integration/debug_test.go b/testutil/integration/debug_test.go index 77d460f05f..ed0ec3d65b 100644 --- a/testutil/integration/debug_test.go +++ b/testutil/integration/debug_test.go @@ -6,11 +6,11 @@ import ( "reflect" "testing" - appConsumer "github.com/cosmos/interchain-security/v4/app/consumer" - appConsumerDemocracy "github.com/cosmos/interchain-security/v4/app/consumer-democracy" - appProvider "github.com/cosmos/interchain-security/v4/app/provider" - integr "github.com/cosmos/interchain-security/v4/tests/integration" - icstestingutils "github.com/cosmos/interchain-security/v4/testutil/ibc_testing" + appConsumer "github.com/cosmos/interchain-security/v5/app/consumer" + appConsumerDemocracy "github.com/cosmos/interchain-security/v5/app/consumer-democracy" + appProvider "github.com/cosmos/interchain-security/v5/app/provider" + integr "github.com/cosmos/interchain-security/v5/tests/integration" + icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" ) // runCCVTestByName runs a single CCV integration test by name, using a CCVTestSuite @@ -283,3 +283,23 @@ func TestHandleConsumerDoubleVotingSlashesUndelegationsAndRelegations(t *testing func TestSlashRetries(t *testing.T) { runCCVTestByName(t, "TestSlashRetries") } + +func TestIBCTransferMiddleware(t *testing.T) { + runCCVTestByName(t, "TestIBCTransferMiddleware") +} + +func TestAllocateTokens(t *testing.T) { + runCCVTestByName(t, "TestAllocateTokens") +} + +func TestTransferConsumerRewardsToDistributionModule(t *testing.T) { + runCCVTestByName(t, "TransferConsumerRewardsToDistributionModule") +} + +func TestAllocateTokensToValidator(t *testing.T) { + runCCVTestByName(t, "TestAllocateTokensToValidator") +} + +func TestMultiConsumerRewardsDistribution(t *testing.T) { + runCCVTestByName(t, "TestMultiConsumerRewardsDistribution") +} diff --git a/testutil/integration/interfaces.go b/testutil/integration/interfaces.go index 89d59904df..83d071a037 100644 --- a/testutil/integration/interfaces.go +++ b/testutil/integration/interfaces.go @@ -20,9 +20,9 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" - providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" + providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // The interface that any provider app must implement to be compatible with ccv integration tests. @@ -142,6 +142,7 @@ type TestDistributionKeeper interface { GetValidatorOutstandingRewards(ctx sdk.Context, val sdk.ValAddress) (rewards distributiontypes.ValidatorOutstandingRewards) GetCommunityTax(ctx sdk.Context) (percent sdk.Dec) + WithdrawValidatorCommission(ctx sdk.Context, valAddr sdk.ValAddress) (sdk.Coins, error) } type TestMintKeeper interface { diff --git a/testutil/keeper/expectations.go b/testutil/keeper/expectations.go index 5a37f3a164..c595a5c080 100644 --- a/testutil/keeper/expectations.go +++ b/testutil/keeper/expectations.go @@ -17,8 +17,8 @@ import ( capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - "github.com/cosmos/interchain-security/v4/x/ccv/types" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // diff --git a/testutil/keeper/mocks.go b/testutil/keeper/mocks.go index 5f9d9b2694..ffa76ad40c 100644 --- a/testutil/keeper/mocks.go +++ b/testutil/keeper/mocks.go @@ -14,13 +14,14 @@ import ( types0 "github.com/cosmos/cosmos-sdk/types" types1 "github.com/cosmos/cosmos-sdk/x/auth/types" types2 "github.com/cosmos/cosmos-sdk/x/capability/types" + types3 "github.com/cosmos/cosmos-sdk/x/distribution/types" v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" - types3 "github.com/cosmos/cosmos-sdk/x/slashing/types" - types4 "github.com/cosmos/cosmos-sdk/x/staking/types" - types5 "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" - types6 "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - types7 "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" - types8 "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + types4 "github.com/cosmos/cosmos-sdk/x/slashing/types" + types5 "github.com/cosmos/cosmos-sdk/x/staking/types" + types6 "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + types7 "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + types8 "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + types9 "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" exported "github.com/cosmos/ibc-go/v7/modules/core/exported" gomock "github.com/golang/mock/gomock" ) @@ -63,10 +64,10 @@ func (mr *MockStakingKeeperMockRecorder) BondDenom(ctx interface{}) *gomock.Call } // Delegation mocks base method. -func (m *MockStakingKeeper) Delegation(ctx types0.Context, addr types0.AccAddress, valAddr types0.ValAddress) types4.DelegationI { +func (m *MockStakingKeeper) Delegation(ctx types0.Context, addr types0.AccAddress, valAddr types0.ValAddress) types5.DelegationI { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Delegation", ctx, addr, valAddr) - ret0, _ := ret[0].(types4.DelegationI) + ret0, _ := ret[0].(types5.DelegationI) return ret0 } @@ -105,10 +106,10 @@ func (mr *MockStakingKeeperMockRecorder) GetLastValidatorPower(ctx, operator int } // GetLastValidators mocks base method. -func (m *MockStakingKeeper) GetLastValidators(ctx types0.Context) []types4.Validator { +func (m *MockStakingKeeper) GetLastValidators(ctx types0.Context) []types5.Validator { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLastValidators", ctx) - ret0, _ := ret[0].([]types4.Validator) + ret0, _ := ret[0].([]types5.Validator) return ret0 } @@ -118,11 +119,26 @@ func (mr *MockStakingKeeperMockRecorder) GetLastValidators(ctx interface{}) *gom return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastValidators", reflect.TypeOf((*MockStakingKeeper)(nil).GetLastValidators), ctx) } +// GetRedelegationByUnbondingID mocks base method. +func (m *MockStakingKeeper) GetRedelegationByUnbondingID(ctx types0.Context, id uint64) (types5.Redelegation, bool) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRedelegationByUnbondingID", ctx, id) + ret0, _ := ret[0].(types5.Redelegation) + ret1, _ := ret[1].(bool) + return ret0, ret1 +} + +// GetRedelegationByUnbondingID indicates an expected call of GetRedelegationByUnbondingID. +func (mr *MockStakingKeeperMockRecorder) GetRedelegationByUnbondingID(ctx, id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRedelegationByUnbondingID", reflect.TypeOf((*MockStakingKeeper)(nil).GetRedelegationByUnbondingID), ctx, id) +} + // GetRedelegationsFromSrcValidator mocks base method. -func (m *MockStakingKeeper) GetRedelegationsFromSrcValidator(ctx types0.Context, valAddr types0.ValAddress) []types4.Redelegation { +func (m *MockStakingKeeper) GetRedelegationsFromSrcValidator(ctx types0.Context, valAddr types0.ValAddress) []types5.Redelegation { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRedelegationsFromSrcValidator", ctx, valAddr) - ret0, _ := ret[0].([]types4.Redelegation) + ret0, _ := ret[0].([]types5.Redelegation) return ret0 } @@ -132,11 +148,26 @@ func (mr *MockStakingKeeperMockRecorder) GetRedelegationsFromSrcValidator(ctx, v return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRedelegationsFromSrcValidator", reflect.TypeOf((*MockStakingKeeper)(nil).GetRedelegationsFromSrcValidator), ctx, valAddr) } +// GetUnbondingDelegationByUnbondingID mocks base method. +func (m *MockStakingKeeper) GetUnbondingDelegationByUnbondingID(ctx types0.Context, id uint64) (types5.UnbondingDelegation, bool) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetUnbondingDelegationByUnbondingID", ctx, id) + ret0, _ := ret[0].(types5.UnbondingDelegation) + ret1, _ := ret[1].(bool) + return ret0, ret1 +} + +// GetUnbondingDelegationByUnbondingID indicates an expected call of GetUnbondingDelegationByUnbondingID. +func (mr *MockStakingKeeperMockRecorder) GetUnbondingDelegationByUnbondingID(ctx, id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUnbondingDelegationByUnbondingID", reflect.TypeOf((*MockStakingKeeper)(nil).GetUnbondingDelegationByUnbondingID), ctx, id) +} + // GetUnbondingDelegationsFromValidator mocks base method. -func (m *MockStakingKeeper) GetUnbondingDelegationsFromValidator(ctx types0.Context, valAddr types0.ValAddress) []types4.UnbondingDelegation { +func (m *MockStakingKeeper) GetUnbondingDelegationsFromValidator(ctx types0.Context, valAddr types0.ValAddress) []types5.UnbondingDelegation { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetUnbondingDelegationsFromValidator", ctx, valAddr) - ret0, _ := ret[0].([]types4.UnbondingDelegation) + ret0, _ := ret[0].([]types5.UnbondingDelegation) return ret0 } @@ -147,10 +178,10 @@ func (mr *MockStakingKeeperMockRecorder) GetUnbondingDelegationsFromValidator(ct } // GetUnbondingType mocks base method. -func (m *MockStakingKeeper) GetUnbondingType(ctx types0.Context, id uint64) (types4.UnbondingType, bool) { +func (m *MockStakingKeeper) GetUnbondingType(ctx types0.Context, id uint64) (types5.UnbondingType, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetUnbondingType", ctx, id) - ret0, _ := ret[0].(types4.UnbondingType) + ret0, _ := ret[0].(types5.UnbondingType) ret1, _ := ret[1].(bool) return ret0, ret1 } @@ -162,10 +193,10 @@ func (mr *MockStakingKeeperMockRecorder) GetUnbondingType(ctx, id interface{}) * } // GetValidator mocks base method. -func (m *MockStakingKeeper) GetValidator(ctx types0.Context, addr types0.ValAddress) (types4.Validator, bool) { +func (m *MockStakingKeeper) GetValidator(ctx types0.Context, addr types0.ValAddress) (types5.Validator, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetValidator", ctx, addr) - ret0, _ := ret[0].(types4.Validator) + ret0, _ := ret[0].(types5.Validator) ret1, _ := ret[1].(bool) return ret0, ret1 } @@ -177,10 +208,10 @@ func (mr *MockStakingKeeperMockRecorder) GetValidator(ctx, addr interface{}) *go } // GetValidatorByConsAddr mocks base method. -func (m *MockStakingKeeper) GetValidatorByConsAddr(ctx types0.Context, consAddr types0.ConsAddress) (types4.Validator, bool) { +func (m *MockStakingKeeper) GetValidatorByConsAddr(ctx types0.Context, consAddr types0.ConsAddress) (types5.Validator, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetValidatorByConsAddr", ctx, consAddr) - ret0, _ := ret[0].(types4.Validator) + ret0, _ := ret[0].(types5.Validator) ret1, _ := ret[1].(bool) return ret0, ret1 } @@ -191,6 +222,21 @@ func (mr *MockStakingKeeperMockRecorder) GetValidatorByConsAddr(ctx, consAddr in return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorByConsAddr", reflect.TypeOf((*MockStakingKeeper)(nil).GetValidatorByConsAddr), ctx, consAddr) } +// GetValidatorByUnbondingID mocks base method. +func (m *MockStakingKeeper) GetValidatorByUnbondingID(ctx types0.Context, id uint64) (types5.Validator, bool) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetValidatorByUnbondingID", ctx, id) + ret0, _ := ret[0].(types5.Validator) + ret1, _ := ret[1].(bool) + return ret0, ret1 +} + +// GetValidatorByUnbondingID indicates an expected call of GetValidatorByUnbondingID. +func (mr *MockStakingKeeperMockRecorder) GetValidatorByUnbondingID(ctx, id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorByUnbondingID", reflect.TypeOf((*MockStakingKeeper)(nil).GetValidatorByUnbondingID), ctx, id) +} + // GetValidatorUpdates mocks base method. func (m *MockStakingKeeper) GetValidatorUpdates(ctx types0.Context) []types.ValidatorUpdate { m.ctrl.T.Helper() @@ -232,7 +278,7 @@ func (mr *MockStakingKeeperMockRecorder) IterateLastValidatorPowers(ctx, cb inte } // IterateValidators mocks base method. -func (m *MockStakingKeeper) IterateValidators(ctx types0.Context, f func(int64, types4.ValidatorI) bool) { +func (m *MockStakingKeeper) IterateValidators(ctx types0.Context, f func(int64, types5.ValidatorI) bool) { m.ctrl.T.Helper() m.ctrl.Call(m, "IterateValidators", ctx, f) } @@ -269,6 +315,20 @@ func (mr *MockStakingKeeperMockRecorder) MaxValidators(ctx interface{}) *gomock. return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaxValidators", reflect.TypeOf((*MockStakingKeeper)(nil).MaxValidators), ctx) } +// MinCommissionRate mocks base method. +func (m *MockStakingKeeper) MinCommissionRate(ctx types0.Context) math.LegacyDec { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MinCommissionRate", ctx) + ret0, _ := ret[0].(math.LegacyDec) + return ret0 +} + +// MinCommissionRate indicates an expected call of MinCommissionRate. +func (mr *MockStakingKeeperMockRecorder) MinCommissionRate(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MinCommissionRate", reflect.TypeOf((*MockStakingKeeper)(nil).MinCommissionRate), ctx) +} + // PowerReduction mocks base method. func (m *MockStakingKeeper) PowerReduction(ctx types0.Context) math.Int { m.ctrl.T.Helper() @@ -312,7 +372,7 @@ func (mr *MockStakingKeeperMockRecorder) Slash(arg0, arg1, arg2, arg3, arg4 inte } // SlashRedelegation mocks base method. -func (m *MockStakingKeeper) SlashRedelegation(arg0 types0.Context, arg1 types4.Validator, arg2 types4.Redelegation, arg3 int64, arg4 types0.Dec) math.Int { +func (m *MockStakingKeeper) SlashRedelegation(arg0 types0.Context, arg1 types5.Validator, arg2 types5.Redelegation, arg3 int64, arg4 types0.Dec) math.Int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SlashRedelegation", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(math.Int) @@ -326,7 +386,7 @@ func (mr *MockStakingKeeperMockRecorder) SlashRedelegation(arg0, arg1, arg2, arg } // SlashUnbondingDelegation mocks base method. -func (m *MockStakingKeeper) SlashUnbondingDelegation(arg0 types0.Context, arg1 types4.UnbondingDelegation, arg2 int64, arg3 types0.Dec) math.Int { +func (m *MockStakingKeeper) SlashUnbondingDelegation(arg0 types0.Context, arg1 types5.UnbondingDelegation, arg2 int64, arg3 types0.Dec) math.Int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SlashUnbondingDelegation", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(math.Int) @@ -340,7 +400,7 @@ func (mr *MockStakingKeeperMockRecorder) SlashUnbondingDelegation(arg0, arg1, ar } // SlashWithInfractionReason mocks base method. -func (m *MockStakingKeeper) SlashWithInfractionReason(arg0 types0.Context, arg1 types0.ConsAddress, arg2, arg3 int64, arg4 types0.Dec, arg5 types4.Infraction) math.Int { +func (m *MockStakingKeeper) SlashWithInfractionReason(arg0 types0.Context, arg1 types0.ConsAddress, arg2, arg3 int64, arg4 types0.Dec, arg5 types5.Infraction) math.Int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SlashWithInfractionReason", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].(math.Int) @@ -394,10 +454,10 @@ func (mr *MockStakingKeeperMockRecorder) Unjail(ctx, addr interface{}) *gomock.C } // Validator mocks base method. -func (m *MockStakingKeeper) Validator(ctx types0.Context, addr types0.ValAddress) types4.ValidatorI { +func (m *MockStakingKeeper) Validator(ctx types0.Context, addr types0.ValAddress) types5.ValidatorI { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Validator", ctx, addr) - ret0, _ := ret[0].(types4.ValidatorI) + ret0, _ := ret[0].(types5.ValidatorI) return ret0 } @@ -408,10 +468,10 @@ func (mr *MockStakingKeeperMockRecorder) Validator(ctx, addr interface{}) *gomoc } // ValidatorByConsAddr mocks base method. -func (m *MockStakingKeeper) ValidatorByConsAddr(ctx types0.Context, consAddr types0.ConsAddress) types4.ValidatorI { +func (m *MockStakingKeeper) ValidatorByConsAddr(ctx types0.Context, consAddr types0.ConsAddress) types5.ValidatorI { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidatorByConsAddr", ctx, consAddr) - ret0, _ := ret[0].(types4.ValidatorI) + ret0, _ := ret[0].(types5.ValidatorI) return ret0 } @@ -459,10 +519,10 @@ func (mr *MockSlashingKeeperMockRecorder) DowntimeJailDuration(arg0 interface{}) } // GetValidatorSigningInfo mocks base method. -func (m *MockSlashingKeeper) GetValidatorSigningInfo(ctx types0.Context, address types0.ConsAddress) (types3.ValidatorSigningInfo, bool) { +func (m *MockSlashingKeeper) GetValidatorSigningInfo(ctx types0.Context, address types0.ConsAddress) (types4.ValidatorSigningInfo, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetValidatorSigningInfo", ctx, address) - ret0, _ := ret[0].(types3.ValidatorSigningInfo) + ret0, _ := ret[0].(types4.ValidatorSigningInfo) ret1, _ := ret[1].(bool) return ret0, ret1 } @@ -473,18 +533,6 @@ func (mr *MockSlashingKeeperMockRecorder) GetValidatorSigningInfo(ctx, address i return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorSigningInfo", reflect.TypeOf((*MockSlashingKeeper)(nil).GetValidatorSigningInfo), ctx, address) } -// SetValidatorSigningInfo mocks base method. -func (m *MockSlashingKeeper) SetValidatorSigningInfo(ctx types0.Context, address types0.ConsAddress, info types3.ValidatorSigningInfo) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "SetValidatorSigningInfo", ctx, address, info) -} - -// SetValidatorSigningInfo indicates an expected call of SetValidatorSigningInfo. -func (mr *MockSlashingKeeperMockRecorder) SetValidatorSigningInfo(ctx, address interface{}, info interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetValidatorSigningInfo", reflect.TypeOf((*MockSlashingKeeper)(nil).SetValidatorSigningInfo), ctx, address, info) -} - // IsTombstoned mocks base method. func (m *MockSlashingKeeper) IsTombstoned(arg0 types0.Context, arg1 types0.ConsAddress) bool { m.ctrl.T.Helper() @@ -511,6 +559,18 @@ func (mr *MockSlashingKeeperMockRecorder) JailUntil(arg0, arg1, arg2 interface{} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "JailUntil", reflect.TypeOf((*MockSlashingKeeper)(nil).JailUntil), arg0, arg1, arg2) } +// SetValidatorSigningInfo mocks base method. +func (m *MockSlashingKeeper) SetValidatorSigningInfo(ctx types0.Context, address types0.ConsAddress, info types4.ValidatorSigningInfo) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetValidatorSigningInfo", ctx, address, info) +} + +// SetValidatorSigningInfo indicates an expected call of SetValidatorSigningInfo. +func (mr *MockSlashingKeeperMockRecorder) SetValidatorSigningInfo(ctx, address, info interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetValidatorSigningInfo", reflect.TypeOf((*MockSlashingKeeper)(nil).SetValidatorSigningInfo), ctx, address, info) +} + // SlashFractionDoubleSign mocks base method. func (m *MockSlashingKeeper) SlashFractionDoubleSign(ctx types0.Context) types0.Dec { m.ctrl.T.Helper() @@ -589,10 +649,10 @@ func (mr *MockChannelKeeperMockRecorder) ChanCloseInit(ctx, portID, channelID, c } // GetChannel mocks base method. -func (m *MockChannelKeeper) GetChannel(ctx types0.Context, srcPort, srcChan string) (types8.Channel, bool) { +func (m *MockChannelKeeper) GetChannel(ctx types0.Context, srcPort, srcChan string) (types9.Channel, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetChannel", ctx, srcPort, srcChan) - ret0, _ := ret[0].(types8.Channel) + ret0, _ := ret[0].(types9.Channel) ret1, _ := ret[1].(bool) return ret0, ret1 } @@ -635,7 +695,7 @@ func (mr *MockChannelKeeperMockRecorder) GetNextSequenceSend(ctx, portID, channe } // SendPacket mocks base method. -func (m *MockChannelKeeper) SendPacket(ctx types0.Context, chanCap *types2.Capability, sourcePort, sourceChannel string, timeoutHeight types6.Height, timeoutTimestamp uint64, data []byte) (uint64, error) { +func (m *MockChannelKeeper) SendPacket(ctx types0.Context, chanCap *types2.Capability, sourcePort, sourceChannel string, timeoutHeight types7.Height, timeoutTimestamp uint64, data []byte) (uint64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendPacket", ctx, chanCap, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, data) ret0, _ := ret[0].(uint64) @@ -724,10 +784,10 @@ func (m *MockConnectionKeeper) EXPECT() *MockConnectionKeeperMockRecorder { } // GetConnection mocks base method. -func (m *MockConnectionKeeper) GetConnection(ctx types0.Context, connectionID string) (types7.ConnectionEnd, bool) { +func (m *MockConnectionKeeper) GetConnection(ctx types0.Context, connectionID string) (types8.ConnectionEnd, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetConnection", ctx, connectionID) - ret0, _ := ret[0].(types7.ConnectionEnd) + ret0, _ := ret[0].(types8.ConnectionEnd) ret1, _ := ret[1].(bool) return ret0, ret1 } @@ -885,6 +945,18 @@ func (m *MockDistributionKeeper) EXPECT() *MockDistributionKeeperMockRecorder { return m.recorder } +// AllocateTokensToValidator mocks base method. +func (m *MockDistributionKeeper) AllocateTokensToValidator(ctx types0.Context, validator types5.ValidatorI, reward types0.DecCoins) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AllocateTokensToValidator", ctx, validator, reward) +} + +// AllocateTokensToValidator indicates an expected call of AllocateTokensToValidator. +func (mr *MockDistributionKeeperMockRecorder) AllocateTokensToValidator(ctx, validator, reward interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllocateTokensToValidator", reflect.TypeOf((*MockDistributionKeeper)(nil).AllocateTokensToValidator), ctx, validator, reward) +} + // FundCommunityPool mocks base method. func (m *MockDistributionKeeper) FundCommunityPool(ctx types0.Context, amount types0.Coins, sender types0.AccAddress) error { m.ctrl.T.Helper() @@ -899,6 +971,46 @@ func (mr *MockDistributionKeeperMockRecorder) FundCommunityPool(ctx, amount, sen return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FundCommunityPool", reflect.TypeOf((*MockDistributionKeeper)(nil).FundCommunityPool), ctx, amount, sender) } +// GetCommunityTax mocks base method. +func (m *MockDistributionKeeper) GetCommunityTax(ctx types0.Context) math.LegacyDec { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCommunityTax", ctx) + ret0, _ := ret[0].(math.LegacyDec) + return ret0 +} + +// GetCommunityTax indicates an expected call of GetCommunityTax. +func (mr *MockDistributionKeeperMockRecorder) GetCommunityTax(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCommunityTax", reflect.TypeOf((*MockDistributionKeeper)(nil).GetCommunityTax), ctx) +} + +// GetFeePool mocks base method. +func (m *MockDistributionKeeper) GetFeePool(ctx types0.Context) types3.FeePool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFeePool", ctx) + ret0, _ := ret[0].(types3.FeePool) + return ret0 +} + +// GetFeePool indicates an expected call of GetFeePool. +func (mr *MockDistributionKeeperMockRecorder) GetFeePool(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFeePool", reflect.TypeOf((*MockDistributionKeeper)(nil).GetFeePool), ctx) +} + +// SetFeePool mocks base method. +func (m *MockDistributionKeeper) SetFeePool(ctx types0.Context, feePool types3.FeePool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetFeePool", ctx, feePool) +} + +// SetFeePool indicates an expected call of SetFeePool. +func (mr *MockDistributionKeeperMockRecorder) SetFeePool(ctx, feePool interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFeePool", reflect.TypeOf((*MockDistributionKeeper)(nil).SetFeePool), ctx, feePool) +} + // MockConsumerHooks is a mock of ConsumerHooks interface. type MockConsumerHooks struct { ctrl *gomock.Controller @@ -1062,10 +1174,10 @@ func (m *MockIBCTransferKeeper) EXPECT() *MockIBCTransferKeeperMockRecorder { } // Transfer mocks base method. -func (m *MockIBCTransferKeeper) Transfer(arg0 context.Context, arg1 *types5.MsgTransfer) (*types5.MsgTransferResponse, error) { +func (m *MockIBCTransferKeeper) Transfer(arg0 context.Context, arg1 *types6.MsgTransfer) (*types6.MsgTransferResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Transfer", arg0, arg1) - ret0, _ := ret[0].(*types5.MsgTransferResponse) + ret0, _ := ret[0].(*types6.MsgTransferResponse) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1100,10 +1212,10 @@ func (m *MockIBCCoreKeeper) EXPECT() *MockIBCCoreKeeperMockRecorder { } // ChannelOpenInit mocks base method. -func (m *MockIBCCoreKeeper) ChannelOpenInit(goCtx context.Context, msg *types8.MsgChannelOpenInit) (*types8.MsgChannelOpenInitResponse, error) { +func (m *MockIBCCoreKeeper) ChannelOpenInit(goCtx context.Context, msg *types9.MsgChannelOpenInit) (*types9.MsgChannelOpenInitResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ChannelOpenInit", goCtx, msg) - ret0, _ := ret[0].(*types8.MsgChannelOpenInitResponse) + ret0, _ := ret[0].(*types9.MsgChannelOpenInitResponse) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/testutil/keeper/unit_test_helpers.go b/testutil/keeper/unit_test_helpers.go index ff3df99763..3a80eecce8 100644 --- a/testutil/keeper/unit_test_helpers.go +++ b/testutil/keeper/unit_test_helpers.go @@ -27,11 +27,11 @@ import ( "github.com/cometbft/cometbft/libs/log" tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - "github.com/cosmos/interchain-security/v4/x/ccv/types" + consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // Parameters needed to instantiate an in-memory keeper @@ -249,10 +249,15 @@ func TestProviderStateIsCleanedAfterConsumerChainIsStopped(t *testing.T, ctx sdk require.Empty(t, providerKeeper.GetAllVscSendTimestamps(ctx, expectedChainID)) + // in case the chain was successfully stopped, it should not contain a Top N associated to it + _, found = providerKeeper.GetTopN(ctx, expectedChainID) + require.False(t, found) + // test key assignment state is cleaned require.Empty(t, providerKeeper.GetAllValidatorConsumerPubKeys(ctx, &expectedChainID)) require.Empty(t, providerKeeper.GetAllValidatorsByConsumerAddr(ctx, &expectedChainID)) require.Empty(t, providerKeeper.GetAllConsumerAddrsToPrune(ctx, expectedChainID)) + require.Empty(t, providerKeeper.GetAllCommissionRateValidators(ctx, expectedChainID)) } func GetTestConsumerAdditionProp() *providertypes.ConsumerAdditionProposal { @@ -271,6 +276,11 @@ func GetTestConsumerAdditionProp() *providertypes.ConsumerAdditionProposal { types.DefaultCCVTimeoutPeriod, types.DefaultTransferTimeoutPeriod, types.DefaultConsumerUnbondingPeriod, + 0, + 0, + 0, + nil, + nil, ).(*providertypes.ConsumerAdditionProposal) return prop diff --git a/x/ccv/consumer/client/cli/query.go b/x/ccv/consumer/client/cli/query.go index 806b91d2cf..3630b3ed3f 100644 --- a/x/ccv/consumer/client/cli/query.go +++ b/x/ccv/consumer/client/cli/query.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" ) // NewQueryCmd returns a root CLI command handler for all x/ccv/provider query commands. diff --git a/x/ccv/consumer/ibc_module.go b/x/ccv/consumer/ibc_module.go index 71caa4a082..4780a0bc73 100644 --- a/x/ccv/consumer/ibc_module.go +++ b/x/ccv/consumer/ibc_module.go @@ -17,9 +17,9 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // OnChanOpenInit implements the IBCModule interface diff --git a/x/ccv/consumer/ibc_module_test.go b/x/ccv/consumer/ibc_module_test.go index 51fd910902..b8868038c9 100644 --- a/x/ccv/consumer/ibc_module_test.go +++ b/x/ccv/consumer/ibc_module_test.go @@ -13,10 +13,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer" - consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer" + consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // TestOnChanOpenInit validates the consumer's OnChanOpenInit implementation against the spec. diff --git a/x/ccv/consumer/keeper/changeover_test.go b/x/ccv/consumer/keeper/changeover_test.go index c431f43477..89d45534dd 100644 --- a/x/ccv/consumer/keeper/changeover_test.go +++ b/x/ccv/consumer/keeper/changeover_test.go @@ -11,8 +11,8 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v4/testutil/crypto" - uthelpers "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v5/testutil/crypto" + uthelpers "github.com/cosmos/interchain-security/v5/testutil/keeper" ) func TestChangeoverToConsumer(t *testing.T) { diff --git a/x/ccv/consumer/keeper/distribution.go b/x/ccv/consumer/keeper/distribution.go index 1b98638498..d6157e50b5 100644 --- a/x/ccv/consumer/keeper/distribution.go +++ b/x/ccv/consumer/keeper/distribution.go @@ -12,8 +12,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // EndBlockRD executes EndBlock logic for the Reward Distribution sub-protocol. diff --git a/x/ccv/consumer/keeper/distribution_test.go b/x/ccv/consumer/keeper/distribution_test.go index 189842a90e..199acae5b0 100644 --- a/x/ccv/consumer/keeper/distribution_test.go +++ b/x/ccv/consumer/keeper/distribution_test.go @@ -10,9 +10,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authTypes "github.com/cosmos/cosmos-sdk/x/auth/types" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // TestGetEstimatedNextFeeDistribution tests next fee distribution parameters. diff --git a/x/ccv/consumer/keeper/genesis.go b/x/ccv/consumer/keeper/genesis.go index 1b9afb2a4e..bdb8e35418 100644 --- a/x/ccv/consumer/keeper/genesis.go +++ b/x/ccv/consumer/keeper/genesis.go @@ -7,8 +7,8 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // InitGenesis initializes the CCV consumer state and binds to PortID. diff --git a/x/ccv/consumer/keeper/genesis_test.go b/x/ccv/consumer/keeper/genesis_test.go index b9fc1b6f3a..77e7e639d3 100644 --- a/x/ccv/consumer/keeper/genesis_test.go +++ b/x/ccv/consumer/keeper/genesis_test.go @@ -18,11 +18,11 @@ import ( abci "github.com/cometbft/cometbft/abci/types" tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v4/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // TestInitGenesis tests that a consumer chain is correctly initialised from genesis. diff --git a/x/ccv/consumer/keeper/grpc_query.go b/x/ccv/consumer/keeper/grpc_query.go index 5ac5116a28..2f1543071b 100644 --- a/x/ccv/consumer/keeper/grpc_query.go +++ b/x/ccv/consumer/keeper/grpc_query.go @@ -8,8 +8,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) var _ types.QueryServer = Keeper{} //nolint:golint diff --git a/x/ccv/consumer/keeper/hooks.go b/x/ccv/consumer/keeper/hooks.go index c730bf266c..715be50e08 100644 --- a/x/ccv/consumer/keeper/hooks.go +++ b/x/ccv/consumer/keeper/hooks.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) var _ ccv.ConsumerHooks = Keeper{} diff --git a/x/ccv/consumer/keeper/keeper.go b/x/ccv/consumer/keeper/keeper.go index b8751344b8..e4f1830333 100644 --- a/x/ccv/consumer/keeper/keeper.go +++ b/x/ccv/consumer/keeper/keeper.go @@ -23,8 +23,8 @@ import ( tmtypes "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/log" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // Keeper defines the Cross-Chain Validation Consumer Keeper diff --git a/x/ccv/consumer/keeper/keeper_test.go b/x/ccv/consumer/keeper/keeper_test.go index 19856211b5..4ece86571d 100644 --- a/x/ccv/consumer/keeper/keeper_test.go +++ b/x/ccv/consumer/keeper/keeper_test.go @@ -17,10 +17,10 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v4/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // TestProviderClientID tests getter and setter functionality for the client ID stored on consumer keeper diff --git a/x/ccv/consumer/keeper/migrations.go b/x/ccv/consumer/keeper/migrations.go index a1e826e61e..71139fb36c 100644 --- a/x/ccv/consumer/keeper/migrations.go +++ b/x/ccv/consumer/keeper/migrations.go @@ -4,7 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - v2 "github.com/cosmos/interchain-security/v4/x/ccv/consumer/migrations/v2" + v2 "github.com/cosmos/interchain-security/v5/x/ccv/consumer/migrations/v2" ) // Migrator is a struct for handling in-place store migrations. diff --git a/x/ccv/consumer/keeper/params.go b/x/ccv/consumer/keeper/params.go index 4d96ddf604..818b2858bc 100644 --- a/x/ccv/consumer/keeper/params.go +++ b/x/ccv/consumer/keeper/params.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // GetParams returns the params for the consumer ccv module diff --git a/x/ccv/consumer/keeper/params_test.go b/x/ccv/consumer/keeper/params_test.go index e2975a0b31..18d3f5a2b2 100644 --- a/x/ccv/consumer/keeper/params_test.go +++ b/x/ccv/consumer/keeper/params_test.go @@ -6,8 +6,8 @@ import ( "github.com/stretchr/testify/require" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // TestParams tests getters/setters for consumer params diff --git a/x/ccv/consumer/keeper/provider_info.go b/x/ccv/consumer/keeper/provider_info.go index e5fbaf6540..aab84e1f85 100644 --- a/x/ccv/consumer/keeper/provider_info.go +++ b/x/ccv/consumer/keeper/provider_info.go @@ -5,8 +5,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) func (k Keeper) GetProviderInfo(ctx sdk.Context) (*types.QueryProviderInfoResponse, error) { //nolint:golint diff --git a/x/ccv/consumer/keeper/relay.go b/x/ccv/consumer/keeper/relay.go index 2d4e16510a..cf431fc054 100644 --- a/x/ccv/consumer/keeper/relay.go +++ b/x/ccv/consumer/keeper/relay.go @@ -14,8 +14,8 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // OnRecvVSCPacket sets the pending validator set changes that will be flushed to ABCI on Endblock diff --git a/x/ccv/consumer/keeper/relay_test.go b/x/ccv/consumer/keeper/relay_test.go index 46a805a85a..b1042de5d0 100644 --- a/x/ccv/consumer/keeper/relay_test.go +++ b/x/ccv/consumer/keeper/relay_test.go @@ -21,11 +21,11 @@ import ( abci "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/bytes" - "github.com/cosmos/interchain-security/v4/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // TestOnRecvVSCPacket tests the behavior of OnRecvVSCPacket over various packet scenarios diff --git a/x/ccv/consumer/keeper/soft_opt_out.go b/x/ccv/consumer/keeper/soft_opt_out.go index e5990ff65d..120d3d46d1 100644 --- a/x/ccv/consumer/keeper/soft_opt_out.go +++ b/x/ccv/consumer/keeper/soft_opt_out.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" ) // BeginBlockSoftOptOut executes BeginBlock logic for the Soft Opt-Out sub-protocol diff --git a/x/ccv/consumer/keeper/soft_opt_out_test.go b/x/ccv/consumer/keeper/soft_opt_out_test.go index 1a726d1767..5c21133832 100644 --- a/x/ccv/consumer/keeper/soft_opt_out_test.go +++ b/x/ccv/consumer/keeper/soft_opt_out_test.go @@ -7,9 +7,9 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v4/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // Tests that UpdateSmallestNonOptOutPower updates the smallest validator power that cannot soft opt out. diff --git a/x/ccv/consumer/keeper/throttle_retry.go b/x/ccv/consumer/keeper/throttle_retry.go index 22e48f9175..4b6df3cc04 100644 --- a/x/ccv/consumer/keeper/throttle_retry.go +++ b/x/ccv/consumer/keeper/throttle_retry.go @@ -5,7 +5,7 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" ) // diff --git a/x/ccv/consumer/keeper/throttle_retry_test.go b/x/ccv/consumer/keeper/throttle_retry_test.go index 4a222fde90..b979ebb51d 100644 --- a/x/ccv/consumer/keeper/throttle_retry_test.go +++ b/x/ccv/consumer/keeper/throttle_retry_test.go @@ -6,9 +6,9 @@ import ( "github.com/stretchr/testify/require" - testutil "github.com/cosmos/interchain-security/v4/testutil/keeper" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + testutil "github.com/cosmos/interchain-security/v5/testutil/keeper" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) func TestPacketSendingPermitted(t *testing.T) { diff --git a/x/ccv/consumer/keeper/validators.go b/x/ccv/consumer/keeper/validators.go index 24a1c5a57c..fa1c8991db 100644 --- a/x/ccv/consumer/keeper/validators.go +++ b/x/ccv/consumer/keeper/validators.go @@ -11,7 +11,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" ) // diff --git a/x/ccv/consumer/keeper/validators_test.go b/x/ccv/consumer/keeper/validators_test.go index 1d4dcb2c86..f1c3dbd3f3 100644 --- a/x/ccv/consumer/keeper/validators_test.go +++ b/x/ccv/consumer/keeper/validators_test.go @@ -15,10 +15,10 @@ import ( tmrand "github.com/cometbft/cometbft/libs/rand" tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v4/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v5/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" ) // TestApplyCCValidatorChanges tests the ApplyCCValidatorChanges method for a consumer keeper diff --git a/x/ccv/consumer/migrations/v2/migration.go b/x/ccv/consumer/migrations/v2/migration.go index 80f06d4d71..ed54512172 100644 --- a/x/ccv/consumer/migrations/v2/migration.go +++ b/x/ccv/consumer/migrations/v2/migration.go @@ -6,8 +6,8 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // MigrateConsumerPacketData migrates consumer packet data according to diff --git a/x/ccv/consumer/migrations/v2/migration_test.go b/x/ccv/consumer/migrations/v2/migration_test.go index a99a2be0fb..8ef05bcbcf 100644 --- a/x/ccv/consumer/migrations/v2/migration_test.go +++ b/x/ccv/consumer/migrations/v2/migration_test.go @@ -9,10 +9,10 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - testutil "github.com/cosmos/interchain-security/v4/testutil/keeper" - v2 "github.com/cosmos/interchain-security/v4/x/ccv/consumer/migrations/v2" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + testutil "github.com/cosmos/interchain-security/v5/testutil/keeper" + v2 "github.com/cosmos/interchain-security/v5/x/ccv/consumer/migrations/v2" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) func TestMigrateConsumerPacketData(t *testing.T) { diff --git a/x/ccv/consumer/module.go b/x/ccv/consumer/module.go index 4b5d9c053b..f522a34fb8 100644 --- a/x/ccv/consumer/module.go +++ b/x/ccv/consumer/module.go @@ -19,10 +19,10 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/client/cli" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/client/cli" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) var ( diff --git a/x/ccv/consumer/types/genesis.go b/x/ccv/consumer/types/genesis.go index cb9cb61f40..9c3fab9c15 100644 --- a/x/ccv/consumer/types/genesis.go +++ b/x/ccv/consumer/types/genesis.go @@ -7,7 +7,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // NewRestartGenesisState returns a consumer GenesisState that has already been established. diff --git a/x/ccv/consumer/types/genesis.pb.go b/x/ccv/consumer/types/genesis.pb.go index dbb7c51981..b51d09efac 100644 --- a/x/ccv/consumer/types/genesis.pb.go +++ b/x/ccv/consumer/types/genesis.pb.go @@ -10,7 +10,7 @@ import ( proto "github.com/cosmos/gogoproto/proto" github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" _07_tendermint "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" - types "github.com/cosmos/interchain-security/v4/x/ccv/types" + types "github.com/cosmos/interchain-security/v5/x/ccv/types" _ "google.golang.org/protobuf/types/known/timestamppb" io "io" math "math" diff --git a/x/ccv/consumer/types/genesis_test.go b/x/ccv/consumer/types/genesis_test.go index dcd30b642c..befd935ba4 100644 --- a/x/ccv/consumer/types/genesis_test.go +++ b/x/ccv/consumer/types/genesis_test.go @@ -15,9 +15,9 @@ import ( abci "github.com/cometbft/cometbft/abci/types" tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v4/testutil/crypto" - "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/testutil/crypto" + "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) const ( diff --git a/x/ccv/consumer/types/keys.go b/x/ccv/consumer/types/keys.go index 20163f5ed9..f6400266e8 100644 --- a/x/ccv/consumer/types/keys.go +++ b/x/ccv/consumer/types/keys.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) const ( diff --git a/x/ccv/consumer/types/params_test.go b/x/ccv/consumer/types/params_test.go index 5b11b52d43..caff4db0b1 100644 --- a/x/ccv/consumer/types/params_test.go +++ b/x/ccv/consumer/types/params_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // Tests the validation of consumer params that happens at genesis diff --git a/x/ccv/consumer/types/query.pb.go b/x/ccv/consumer/types/query.pb.go index fe62a4217f..29f7d33971 100644 --- a/x/ccv/consumer/types/query.pb.go +++ b/x/ccv/consumer/types/query.pb.go @@ -9,7 +9,7 @@ import ( _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" proto "github.com/cosmos/gogoproto/proto" - types "github.com/cosmos/interchain-security/v4/x/ccv/types" + types "github.com/cosmos/interchain-security/v5/x/ccv/types" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" diff --git a/x/ccv/democracy/distribution/module.go b/x/ccv/democracy/distribution/module.go index af35c86846..b30893402b 100644 --- a/x/ccv/democracy/distribution/module.go +++ b/x/ccv/democracy/distribution/module.go @@ -16,7 +16,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" ) var ( diff --git a/x/ccv/provider/client/cli/query.go b/x/ccv/provider/client/cli/query.go index 90f7450355..575470c6f3 100644 --- a/x/ccv/provider/client/cli/query.go +++ b/x/ccv/provider/client/cli/query.go @@ -11,7 +11,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) // NewQueryCmd returns a root CLI command handler for all x/ccv/provider query commands. @@ -35,6 +35,9 @@ func NewQueryCmd() *cobra.Command { cmd.AddCommand(CmdProposedConsumerChains()) cmd.AddCommand(CmdAllPairsValConAddrByConsumerChainID()) cmd.AddCommand(CmdProviderParameters()) + cmd.AddCommand(CmdConsumerChainOptedInValidators()) + cmd.AddCommand(CmdConsumerChainsValidatorHasToValidate()) + cmd.AddCommand(CmdValidatorConsumerCommissionRate()) cmd.AddCommand(CmdOldestUnconfirmedVsc()) return cmd } @@ -411,6 +414,126 @@ $ %s query provider params return cmd } +// Command to query opted-in validators by consumer chain ID +func CmdConsumerChainOptedInValidators() *cobra.Command { + cmd := &cobra.Command{ + Use: "consumer-opted-in-validators [chainid]", + Short: "Query opted-in validators for a given consumer chain", + Long: strings.TrimSpace( + fmt.Sprintf(`Query opted-in validators for a given consumer chain. +Example: +$ %s consumer-opted-in-validators foochain + `, version.AppName), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.QueryConsumerChainOptedInValidators(cmd.Context(), + &types.QueryConsumerChainOptedInValidatorsRequest{ChainId: args[0]}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// Command to query the consumer chains list a given validator has to validate +func CmdConsumerChainsValidatorHasToValidate() *cobra.Command { + bech32PrefixConsAddr := sdk.GetConfig().GetBech32ConsensusAddrPrefix() + cmd := &cobra.Command{ + Use: "has-to-validate [provider-validator-address]", + Short: "Query the consumer chains list a given validator has to validate", + Long: strings.TrimSpace( + fmt.Sprintf(`the list of consumer chains that as a validator, you need to be running right now, is always a subset of this, so it seems like a very nice "safe bet". +Example: +$ %s has-to-validate %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj + `, version.AppName, bech32PrefixConsAddr), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + addr, err := sdk.ConsAddressFromBech32(args[0]) + if err != nil { + return err + } + + res, err := queryClient.QueryConsumerChainsValidatorHasToValidate(cmd.Context(), + &types.QueryConsumerChainsValidatorHasToValidateRequest{ + ProviderAddress: addr.String(), + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// Command to query the consumer commission rate a validator charges +// on a consumer chain +func CmdValidatorConsumerCommissionRate() *cobra.Command { + bech32PrefixConsAddr := sdk.GetConfig().GetBech32ConsensusAddrPrefix() + cmd := &cobra.Command{ + Use: "validator-consumer-commission-rate [chainid] [provider-validator-address]", + Short: "Query the consumer commission rate a validator charges on a consumer chain", + Long: strings.TrimSpace( + fmt.Sprintf(`Query the consumer commission rate a validator charges on a consumer chain. +Example: +$ %s validator-consumer-commission-rate foochain %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj + `, version.AppName, bech32PrefixConsAddr), + ), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + addr, err := sdk.ConsAddressFromBech32(args[1]) + if err != nil { + return err + } + + res, err := queryClient.QueryValidatorConsumerCommissionRate(cmd.Context(), + &types.QueryValidatorConsumerCommissionRateRequest{ + ChainId: args[0], + ProviderAddress: addr.String(), + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + func CmdOldestUnconfirmedVsc() *cobra.Command { cmd := &cobra.Command{ Use: "oldest_unconfirmed_vsc [chainid]", diff --git a/x/ccv/provider/client/cli/tx.go b/x/ccv/provider/client/cli/tx.go index 379e55a792..ef65abc28e 100644 --- a/x/ccv/provider/client/cli/tx.go +++ b/x/ccv/provider/client/cli/tx.go @@ -18,7 +18,7 @@ import ( tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) // GetTxCmd returns the transaction commands for this module @@ -34,6 +34,9 @@ func GetTxCmd() *cobra.Command { cmd.AddCommand(NewAssignConsumerKeyCmd()) cmd.AddCommand(NewSubmitConsumerMisbehaviourCmd()) cmd.AddCommand(NewSubmitConsumerDoubleVotingCmd()) + cmd.AddCommand(NewOptInCmd()) + cmd.AddCommand(NewOptOutCmd()) + cmd.AddCommand(NewSetConsumerCommissionRateCmd()) return cmd } @@ -202,3 +205,132 @@ Example: return cmd } + +func NewOptInCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "opt-in [consumer-chain-id] [consumer-pubkey]", + Short: "opts in validator to the consumer chain, and if given uses the " + + "provided consensus public key for this consumer chain", + Args: cobra.RangeArgs(1, 2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + txf, err := tx.NewFactoryCLI(clientCtx, cmd.Flags()) + if err != nil { + return err + } + txf = txf.WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever) + + providerValAddr := clientCtx.GetFromAddress() + + var consumerPubKey string + if len(args) == 2 { + // consumer public key was provided + consumerPubKey = args[1] + } else { + consumerPubKey = "" + } + msg, err := types.NewMsgOptIn(args[0], sdk.ValAddress(providerValAddr), consumerPubKey) + if err != nil { + return err + } + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + _ = cmd.MarkFlagRequired(flags.FlagFrom) + + return cmd +} + +func NewOptOutCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "opt-out [consumer-chain-id]", + Short: "opts out validator from this consumer chain", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + txf, err := tx.NewFactoryCLI(clientCtx, cmd.Flags()) + if err != nil { + return err + } + txf = txf.WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever) + + providerValAddr := clientCtx.GetFromAddress() + + msg, err := types.NewMsgOptOut(args[0], sdk.ValAddress(providerValAddr)) + if err != nil { + return err + } + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + _ = cmd.MarkFlagRequired(flags.FlagFrom) + + return cmd +} + +func NewSetConsumerCommissionRateCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "set-consumer-commission-rate [consumer-chain-id] [commission-rate]", + Short: "set a per-consumer chain commission", + Long: strings.TrimSpace( + fmt.Sprintf(`Note that the "commission-rate" argument is a fraction and should be in the range [0,1]. + Example: + %s set-consumer-commission-rate consumer-1 0.5 --from node0 --home ../node0`, + version.AppName), + ), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + txf, err := tx.NewFactoryCLI(clientCtx, cmd.Flags()) + if err != nil { + return err + } + txf = txf.WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever) + + providerValAddr := clientCtx.GetFromAddress() + + commission, err := sdk.NewDecFromStr(args[1]) + if err != nil { + return err + } + msg := types.NewMsgSetConsumerCommissionRate(args[0], commission, sdk.ValAddress(providerValAddr)) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + _ = cmd.MarkFlagRequired(flags.FlagFrom) + + return cmd +} diff --git a/x/ccv/provider/client/proposal_handler.go b/x/ccv/provider/client/proposal_handler.go index fa74bb953e..9942a6d431 100644 --- a/x/ccv/provider/client/proposal_handler.go +++ b/x/ccv/provider/client/proposal_handler.go @@ -20,7 +20,7 @@ import ( govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) var ( @@ -64,7 +64,12 @@ Where proposal.json contains: "transfer_timeout_period": 3600000000000, "ccv_timeout_period": 2419200000000000, "unbonding_period": 1728000000000000, - "deposit": "10000stake" + "deposit": "10000stake", + "top_n": 0, + "validators_power_cap": 32, + "validator_set_cap": 50, + "allowlist": [], + "denylist": ["validatorAConsensusAddress", "validatorBConsensusAddress"] } `, RunE: func(cmd *cobra.Command, args []string) error { @@ -86,7 +91,8 @@ Where proposal.json contains: proposal.GenesisHash, proposal.BinaryHash, proposal.SpawnTime, proposal.ConsumerRedistributionFraction, proposal.BlocksPerDistributionTransmission, proposal.DistributionTransmissionChannel, proposal.HistoricalEntries, - proposal.CcvTimeoutPeriod, proposal.TransferTimeoutPeriod, proposal.UnbondingPeriod) + proposal.CcvTimeoutPeriod, proposal.TransferTimeoutPeriod, proposal.UnbondingPeriod, proposal.TopN, + proposal.ValidatorsPowerCap, proposal.ValidatorSetCap, proposal.Allowlist, proposal.Denylist) from := clientCtx.GetFromAddress() @@ -241,6 +247,12 @@ type ConsumerAdditionProposalJSON struct { UnbondingPeriod time.Duration `json:"unbonding_period"` Deposit string `json:"deposit"` + + TopN uint32 `json:"top_N"` + ValidatorsPowerCap uint32 `json:"validators_power_cap"` + ValidatorSetCap uint32 `json:"validator_set_cap"` + Allowlist []string `json:"allowlist"` + Denylist []string `json:"denylist"` } type ConsumerAdditionProposalReq struct { diff --git a/x/ccv/provider/handler.go b/x/ccv/provider/handler.go index cf176a86c1..72abf10c1c 100644 --- a/x/ccv/provider/handler.go +++ b/x/ccv/provider/handler.go @@ -6,8 +6,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) func NewHandler(k *keeper.Keeper) sdk.Handler { @@ -26,6 +26,15 @@ func NewHandler(k *keeper.Keeper) sdk.Handler { case *types.MsgSubmitConsumerDoubleVoting: res, err := msgServer.SubmitConsumerDoubleVoting(sdk.WrapSDKContext(ctx), msg) return sdk.WrapServiceResult(ctx, res, err) + case *types.MsgOptIn: + res, err := msgServer.OptIn(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + case *types.MsgOptOut: + res, err := msgServer.OptOut(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + case *types.MsgSetConsumerCommissionRate: + res, err := msgServer.SetConsumerCommissionRate(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) default: return nil, errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg) } diff --git a/x/ccv/provider/handler_test.go b/x/ccv/provider/handler_test.go index 8cefa3f949..2351b6c61c 100644 --- a/x/ccv/provider/handler_test.go +++ b/x/ccv/provider/handler_test.go @@ -14,11 +14,11 @@ import ( tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - testcrypto "github.com/cosmos/interchain-security/v4/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/provider" - keeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + testcrypto "github.com/cosmos/interchain-security/v5/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider" + keeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) func TestInvalidMsg(t *testing.T) { @@ -34,6 +34,10 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { providerCryptoId := testcrypto.NewCryptoIdentityFromIntSeed(0) providerConsAddr := providerCryptoId.ProviderConsAddress() + // a different providerConsAddr, to simulate different validators having assigned keys + providerCryptoId2 := testcrypto.NewCryptoIdentityFromIntSeed(10) + providerConsAddr2 := providerCryptoId2.ProviderConsAddress() + consumerCryptoId := testcrypto.NewCryptoIdentityFromIntSeed(1) consumerConsAddr := consumerCryptoId.ConsumerConsAddress() consumerKeyBz := base64.StdEncoding.EncodeToString(consumerCryptoId.ConsensusSDKPubKey().Bytes()) @@ -101,7 +105,32 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { chainID: "chainid", }, { - name: "fail: consumer key in use", + name: "fail: consumer key in use by other validator", + setup: func(ctx sdk.Context, + k keeper.Keeper, mocks testkeeper.MockedKeepers, + ) { + k.SetPendingConsumerAdditionProp(ctx, &providertypes.ConsumerAdditionProposal{ + ChainId: "chainid", + }) + // Use the consumer key already used by some other validator + k.SetValidatorByConsumerAddr(ctx, "chainid", consumerConsAddr, providerConsAddr2) + + gomock.InOrder( + mocks.MockStakingKeeper.EXPECT().GetValidator( + ctx, providerCryptoId.SDKValOpAddress(), + // validator should not be missing + ).Return(providerCryptoId.SDKStakingValidator(), true).Times(1), + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, + consumerConsAddr.ToSdkConsAddr(), + // return false - no other validator uses the consumer key to validate *on the provider* + ).Return(stakingtypes.Validator{}, false), + ) + }, + expError: true, + chainID: "chainid", + }, + { + name: "success: consumer key in use, but by the same validator", setup: func(ctx sdk.Context, k keeper.Keeper, mocks testkeeper.MockedKeepers, ) { @@ -121,7 +150,7 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { ).Return(stakingtypes.Validator{}, false), ) }, - expError: true, + expError: false, chainID: "chainid", }, } diff --git a/x/ccv/provider/ibc_middleware.go b/x/ccv/provider/ibc_middleware.go new file mode 100644 index 0000000000..a1ef6e0db8 --- /dev/null +++ b/x/ccv/provider/ibc_middleware.go @@ -0,0 +1,242 @@ +package provider + +import ( + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + + "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + + "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" +) + +var _ porttypes.Middleware = &IBCMiddleware{} + +// IBCMiddleware implements the callbacks for the IBC transfer middleware given the +// provider keeper and the underlying application. +type IBCMiddleware struct { + app porttypes.IBCModule + keeper keeper.Keeper +} + +// NewIBCMiddleware creates a new IBCMiddlware given the keeper and underlying application +func NewIBCMiddleware(app porttypes.IBCModule, k keeper.Keeper) IBCMiddleware { + return IBCMiddleware{ + app: app, + keeper: k, + } +} + +// OnChanOpenInit implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanOpenInit( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID string, + channelID string, + chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version string, +) (string, error) { + // call underlying app's OnChanOpenInit callback with the appVersion + return im.app.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, version) +} + +// OnChanOpenTry implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanOpenTry( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID, + channelID string, + chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + counterpartyVersion string, +) (string, error) { + // call underlying app's OnChanOpenTry callback with the appVersion + return im.app.OnChanOpenTry(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, counterpartyVersion) +} + +// OnChanOpenAck implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanOpenAck( + ctx sdk.Context, + portID, + channelID string, + counterpartyChannelID string, + counterpartyVersion string, +) error { + // call underlying app's OnChanOpenAck callback with the counterparty app version. + return im.app.OnChanOpenAck(ctx, portID, channelID, counterpartyChannelID, counterpartyVersion) +} + +// OnChanOpenConfirm implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanOpenConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + // call underlying app's OnChanOpenConfirm callback. + return im.app.OnChanOpenConfirm(ctx, portID, channelID) +} + +// OnChanCloseInit implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanCloseInit( + ctx sdk.Context, + portID, + channelID string, +) error { + // call underlying app's OnChanCloseInit callback. + return im.app.OnChanCloseInit(ctx, portID, channelID) +} + +// OnChanCloseConfirm implements the IBCMiddleware interface +func (im IBCMiddleware) OnChanCloseConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + return im.app.OnChanCloseConfirm(ctx, portID, channelID) +} + +// OnRecvPacket executes the IBC transfer. In case of success, +// it verifies if the packet sender is a consumer chain +// and if the received IBC coin is whitelisted. In such instances, +// it appends the coin to the consumer's chain allocation record +func (im IBCMiddleware) OnRecvPacket( + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, +) exported.Acknowledgement { + // executes the IBC transfer OnRecv logic + ack := im.app.OnRecvPacket(ctx, packet, relayer) + + // Note that inside the below if condition statement, + // we know that the IBC transfer succeeded. That entails + // that the packet data is valid and can be safely + // deserialized without checking errors. + if ack.Success() { + // execute the middleware logic only if the sender is a consumer chain + consumerID, err := im.keeper.IdentifyConsumerChainIDFromIBCPacket(ctx, packet) + if err != nil { + return ack + } + + // extract the coin info received from the packet data + var data ibctransfertypes.FungibleTokenPacketData + _ = types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data) + + // check if the recipient is the consumer reward's pool address + receiver, _ := sdk.AccAddressFromBech32(data.Receiver) + if receiver.String() != im.keeper.GetConsumerRewardsPoolAddressStr(ctx) { + return ack + } + + coinAmt, _ := math.NewIntFromString(data.Amount) + coinDenom := GetProviderDenom(data.Denom, packet) + + // verify that the coin's denom is a whitelisted consumer denom, + // and if so, adds it to the consumer chain rewards allocation, + // otherwise the prohibited coin just stays in the pool forever. + if im.keeper.ConsumerRewardDenomExists(ctx, coinDenom) { + alloc := im.keeper.GetConsumerRewardsAllocation(ctx, consumerID) + alloc.Rewards = alloc.Rewards.Add( + sdk.NewDecCoinsFromCoins(sdk.Coin{ + Denom: coinDenom, + Amount: coinAmt, + })...) + im.keeper.SetConsumerRewardsAllocation(ctx, consumerID, alloc) + } + } + + return ack +} + +// OnAcknowledgementPacket implements the IBCMiddleware interface +// If fees are not enabled, this callback will default to the ibc-core packet callback +func (im IBCMiddleware) OnAcknowledgementPacket( + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, + relayer sdk.AccAddress, +) error { + // call underlying app's OnAcknowledgementPacket callback. + return im.app.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer) +} + +// OnTimeoutPacket implements the IBCMiddleware interface +// If fees are not enabled, this callback will default to the ibc-core packet callback +func (im IBCMiddleware) OnTimeoutPacket( + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, +) error { + // call underlying app's OnTimeoutPacket callback. + return im.app.OnTimeoutPacket(ctx, packet, relayer) +} + +// SendPacket implements the ICS4 Wrapper interface +func (im IBCMiddleware) SendPacket( + sdk.Context, + *capabilitytypes.Capability, + string, + string, + clienttypes.Height, + uint64, + []byte, +) (uint64, error) { + panic("should never be called since the IBC middleware doesn't have an ICS4wrapper") +} + +// WriteAcknowledgement implements the ICS4 Wrapper interface +func (im IBCMiddleware) WriteAcknowledgement( + ctx sdk.Context, + chanCap *capabilitytypes.Capability, + packet exported.PacketI, + ack exported.Acknowledgement, +) error { + panic("should never be called since the IBC middleware doesn't have an ICS4wrapper") +} + +// GetAppVersion returns the application version of the underlying application +func (im IBCMiddleware) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) { + panic("should never be called since the IBC middleware doesn't have an ICS4wrapper") +} + +// GetProviderDenom returns the updated given denom according to the given IBC packet +// It follows the same logic than the OnRecvPacket method of the IBC transfer module +// see https://github.com/cosmos/ibc-go/blob/v7.3.2/modules/apps/transfer/keeper/relay.go#L162 +func GetProviderDenom(denom string, packet channeltypes.Packet) (providerDenom string) { + // If the the prefix denom corresponds to the packet's source port and channel, + // returns the base denom + if ibctransfertypes.ReceiverChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), denom) { + voucherPrefix := ibctransfertypes.GetDenomPrefix(packet.GetSourcePort(), packet.GetSourceChannel()) + unprefixedDenom := denom[len(voucherPrefix):] + + // coin denomination used in sending from the escrow address + providerDenom = unprefixedDenom + + // The denomination used to send the coins is either the native denom or the hash of the path + // if the denomination is not native. + denomTrace := ibctransfertypes.ParseDenomTrace(unprefixedDenom) + if denomTrace.Path != "" { + providerDenom = denomTrace.IBCDenom() + } + // update the prefix denom according to the packet info + } else { + prefixedDenom := ibctransfertypes.GetPrefixedDenom( + packet.GetDestPort(), + packet.GetDestChannel(), + denom, + ) + + providerDenom = ibctransfertypes.ParseDenomTrace(prefixedDenom).IBCDenom() + } + + return providerDenom +} diff --git a/x/ccv/provider/ibc_middleware_test.go b/x/ccv/provider/ibc_middleware_test.go new file mode 100644 index 0000000000..e2be5a649d --- /dev/null +++ b/x/ccv/provider/ibc_middleware_test.go @@ -0,0 +1,77 @@ +package provider_test + +import ( + "testing" + + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + "github.com/stretchr/testify/require" + + "github.com/cosmos/interchain-security/v5/x/ccv/provider" +) + +func TestGetProviderDenom(t *testing.T) { + testCases := []struct { + name string + denom string + packet channeltypes.Packet + expProviderDenom string + }{ + { + name: "returns base denom with destination port and channel as prefix", + denom: "stake", + packet: channeltypes.NewPacket( + []byte{}, + 0, + "srcPort", + "srcChannel", + "dstPort", + "dstChannel", + clienttypes.NewHeight(1, 1), + 0, + ), + expProviderDenom: "dstPort/dstChannel/stake", + }, + { + name: "returns base denom if the prefix denom corresponds to the packet's port and channel source", + denom: "srcPort/srcChannel/stake", + packet: channeltypes.NewPacket( + []byte{}, + 0, + "srcPort", + "srcChannel", + "dstPort", + "dstChannel", + clienttypes.NewHeight(1, 1), + 0, + ), + expProviderDenom: "stake", + }, + { + name: "returns prefixed denom updated with packet's port and channel destination as prefix", + denom: "dstPort/dstChannel/stake", + packet: channeltypes.NewPacket( + []byte{}, + 0, + "srcPort", + "srcChannel", + "dstPort", + "dstChannel", + clienttypes.NewHeight(1, 1), + 0, + ), + expProviderDenom: "dstPort/dstChannel/dstPort/dstChannel/stake", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + res := provider.GetProviderDenom( + tc.denom, + tc.packet, + ) + + require.Equal(t, tc.expProviderDenom, res) + }) + } +} diff --git a/x/ccv/provider/ibc_module.go b/x/ccv/provider/ibc_module.go index f6ad43d60b..f4b1eace4d 100644 --- a/x/ccv/provider/ibc_module.go +++ b/x/ccv/provider/ibc_module.go @@ -15,9 +15,9 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // OnChanOpenInit implements the IBCModule interface diff --git a/x/ccv/provider/ibc_module_test.go b/x/ccv/provider/ibc_module_test.go index df12ed4cb8..9d41a4d9e1 100644 --- a/x/ccv/provider/ibc_module_test.go +++ b/x/ccv/provider/ibc_module_test.go @@ -15,11 +15,11 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/provider" - providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider" + providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // TestOnChanOpenInit tests the provider's OnChanOpenInit method against spec. diff --git a/x/ccv/provider/keeper/consumer_equivocation.go b/x/ccv/provider/keeper/consumer_equivocation.go index 8fc9808304..6324dfa5c0 100644 --- a/x/ccv/provider/keeper/consumer_equivocation.go +++ b/x/ccv/provider/keeper/consumer_equivocation.go @@ -19,8 +19,8 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // diff --git a/x/ccv/provider/keeper/consumer_equivocation_test.go b/x/ccv/provider/keeper/consumer_equivocation_test.go index d92caf69a3..13b56fc04a 100644 --- a/x/ccv/provider/keeper/consumer_equivocation_test.go +++ b/x/ccv/provider/keeper/consumer_equivocation_test.go @@ -18,9 +18,9 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) func TestVerifyDoubleVotingEvidence(t *testing.T) { diff --git a/x/ccv/provider/keeper/distribution.go b/x/ccv/provider/keeper/distribution.go index 5b2e6025ef..49137936eb 100644 --- a/x/ccv/provider/keeper/distribution.go +++ b/x/ccv/provider/keeper/distribution.go @@ -1,17 +1,27 @@ package keeper import ( + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) -// EndBlockRD executes EndBlock logic for the Reward Distribution sub-protocol. -// Reward Distribution follows a simple model: send tokens to the ConsumerRewardsPool, -// from where they sent to the fee collector address -func (k Keeper) EndBlockRD(ctx sdk.Context) { - // transfers all whitelisted consumer rewards to the fee collector address - k.TransferRewardsToFeeCollector(ctx) +// BeginBlockRD executes BeginBlock logic for the Reward Distribution sub-protocol. +func (k Keeper) BeginBlockRD(ctx sdk.Context, req abci.RequestBeginBlock) { + // TODO this is Tendermint-dependent + // ref https://github.com/cosmos/cosmos-sdk/issues/3095 + if ctx.BlockHeight() > 1 { + k.AllocateTokens(ctx) + } } func (k Keeper) GetConsumerRewardsPoolAddressStr(ctx sdk.Context) string { @@ -57,32 +67,232 @@ func (k Keeper) GetAllConsumerRewardDenoms(ctx sdk.Context) (consumerRewardDenom return consumerRewardDenoms } -// TransferRewardsToFeeCollector transfers all consumer rewards to the fee collector address -func (k Keeper) TransferRewardsToFeeCollector(ctx sdk.Context) { - // 1. Get the denom whitelist from the store - denoms := k.GetAllConsumerRewardDenoms(ctx) +// AllocateTokens performs rewards distribution to the community pool and validators +// based on the Partial Set Security distribution specification. +func (k Keeper) AllocateTokens(ctx sdk.Context) { + // return if there is no coins in the consumer rewards pool + if k.GetConsumerRewardsPool(ctx).IsZero() { + return + } + + // Iterate over all registered consumer chains + for _, consumer := range k.GetAllConsumerChains(ctx) { + // transfer the consumer rewards to the distribution module account + // note that the rewards transferred are only consumer whitelisted denoms + rewardsCollected, err := k.TransferConsumerRewardsToDistributionModule(ctx, consumer.ChainId) + if err != nil { + k.Logger(ctx).Error( + "fail to transfer rewards to distribution module for chain %s: %s", + consumer.ChainId, + err, + ) + continue + } + + // note that it's possible that no rewards are collected even though the + // reward pool isn't empty. This can happen if the reward pool holds some tokens + // of non-whitelisted denominations. + if rewardsCollected.IsZero() { + continue + } + + rewardsCollectedDec := sdk.NewDecCoinsFromCoins(rewardsCollected...) + + // temporary workaround to keep CanWithdrawInvariant happy + // general discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634 + feePool := k.distributionKeeper.GetFeePool(ctx) + if k.ComputeConsumerTotalVotingPower(ctx, consumer.ChainId) == 0 { + feePool.CommunityPool = feePool.CommunityPool.Add(rewardsCollectedDec...) + k.distributionKeeper.SetFeePool(ctx, feePool) + return + } + + // calculate the reward allocations + remaining := rewardsCollectedDec + communityTax := k.distributionKeeper.GetCommunityTax(ctx) + voteMultiplier := math.LegacyOneDec().Sub(communityTax) + feeMultiplier := rewardsCollectedDec.MulDecTruncate(voteMultiplier) - // 2. Iterate over the whitelist - for _, denom := range denoms { - // 3. For each denom, retrieve the balance from the consumer rewards pool - balance := k.bankKeeper.GetBalance( + // allocate tokens to consumer validators + feeAllocated := k.AllocateTokensToConsumerValidators( ctx, - k.accountKeeper.GetModuleAccount(ctx, types.ConsumerRewardsPool).GetAddress(), - denom, + consumer.ChainId, + feeMultiplier, ) + remaining = remaining.Sub(feeAllocated) - // if the balance is not zero, - if !balance.IsZero() { - // 4. Transfer the balance to the fee collector address - err := k.bankKeeper.SendCoinsFromModuleToModule( - ctx, - types.ConsumerRewardsPool, - k.feeCollectorName, - sdk.NewCoins(balance), - ) - if err != nil { - k.Logger(ctx).Error("cannot sent consumer rewards to fee collector:", "reward", balance.String()) - } + // allocate community funding + feePool.CommunityPool = feePool.CommunityPool.Add(remaining...) + k.distributionKeeper.SetFeePool(ctx, feePool) + } +} + +// AllocateTokensToConsumerValidators allocates tokens +// to the given consumer chain's validator set +func (k Keeper) AllocateTokensToConsumerValidators( + ctx sdk.Context, + chainID string, + tokens sdk.DecCoins, +) (allocated sdk.DecCoins) { + // return early if the tokens are empty + if tokens.Empty() { + return allocated + } + + // get the total voting power of the consumer valset + totalPower := k.ComputeConsumerTotalVotingPower(ctx, chainID) + if totalPower == 0 { + return allocated + } + + // Allocate tokens by iterating over the consumer validators + for _, consumerVal := range k.GetConsumerValSet(ctx, chainID) { + consAddr := sdk.ConsAddress(consumerVal.ProviderConsAddr) + + // get the validator tokens fraction using its voting power + powerFraction := math.LegacyNewDec(consumerVal.Power).QuoTruncate(math.LegacyNewDec(totalPower)) + tokensFraction := tokens.MulDecTruncate(powerFraction) + + // get the validator type struct for the consensus address + val := k.stakingKeeper.ValidatorByConsAddr(ctx, consAddr).(stakingtypes.Validator) + + // check if the validator set a custom commission rate for the consumer chain + if cr, found := k.GetConsumerCommissionRate(ctx, chainID, types.NewProviderConsAddress(consAddr)); found { + // set the validator commission rate + val.Commission.CommissionRates.Rate = cr } + + // allocate the consumer reward tokens to the validator + k.distributionKeeper.AllocateTokensToValidator( + ctx, + val, + tokensFraction, + ) + + // sum the tokens allocated + allocated = allocated.Add(tokensFraction...) + } + + return allocated +} + +// TransferConsumerRewardsToDistributionModule transfers the rewards allocation of the given consumer chain +// from the consumer rewards pool to a the distribution module +func (k Keeper) TransferConsumerRewardsToDistributionModule( + ctx sdk.Context, + chainID string, +) (sdk.Coins, error) { + // Get coins of the consumer rewards allocation + allocation := k.GetConsumerRewardsAllocation(ctx, chainID) + + if allocation.Rewards.IsZero() { + return sdk.Coins{}, nil + } + + // Truncate coin rewards + rewardsToSend, _ := allocation.Rewards.TruncateDecimal() + + // NOTE the consumer rewards allocation isn't a module account, however its coins + // are held in the consumer reward pool module account. Thus the consumer + // rewards allocation must be reduced separately from the SendCoinsFromModuleToAccount call. + + // Update consumer rewards allocation with the remaining decimal coins + allocation.Rewards = allocation.Rewards.Sub(sdk.NewDecCoinsFromCoins(rewardsToSend...)) + + // Send coins to distribution module account + err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.ConsumerRewardsPool, distrtypes.ModuleName, rewardsToSend) + if err != nil { + return sdk.Coins{}, err + } + + k.SetConsumerRewardsAllocation(ctx, chainID, allocation) + return rewardsToSend, nil +} + +// consumer reward pools getter and setter + +// GetConsumerRewardsAllocation returns the consumer rewards allocation for the given chain ID +func (k Keeper) GetConsumerRewardsAllocation(ctx sdk.Context, chainID string) (pool types.ConsumerRewardsAllocation) { + store := ctx.KVStore(k.storeKey) + b := store.Get(types.ConsumerRewardsAllocationKey(chainID)) + k.cdc.MustUnmarshal(b, &pool) + return +} + +// SetConsumerRewardsAllocation sets the consumer rewards allocation for the given chain ID +func (k Keeper) SetConsumerRewardsAllocation(ctx sdk.Context, chainID string, pool types.ConsumerRewardsAllocation) { + store := ctx.KVStore(k.storeKey) + b := k.cdc.MustMarshal(&pool) + store.Set(types.ConsumerRewardsAllocationKey(chainID), b) +} + +// GetConsumerRewardsPool returns the balance +// of the consumer rewards pool module account +func (k Keeper) GetConsumerRewardsPool(ctx sdk.Context) sdk.Coins { + return k.bankKeeper.GetAllBalances( + ctx, + k.accountKeeper.GetModuleAccount(ctx, types.ConsumerRewardsPool).GetAddress(), + ) +} + +// ComputeConsumerTotalVotingPower returns the validator set total voting power +// for the given consumer chain +func (k Keeper) ComputeConsumerTotalVotingPower(ctx sdk.Context, chainID string) (totalPower int64) { + // sum the opted-in validators set voting powers + for _, v := range k.GetConsumerValSet(ctx, chainID) { + totalPower += v.Power + } + + return +} + +// IdentifyConsumerChainIDFromIBCPacket checks if the packet destination matches a registered consumer chain. +// If so, it returns the consumer chain ID, otherwise an error. +func (k Keeper) IdentifyConsumerChainIDFromIBCPacket(ctx sdk.Context, packet channeltypes.Packet) (string, error) { + channel, ok := k.channelKeeper.GetChannel(ctx, packet.DestinationPort, packet.DestinationChannel) + if !ok { + return "", errorsmod.Wrapf(channeltypes.ErrChannelNotFound, "channel not found for channel ID: %s", packet.DestinationChannel) + } + if len(channel.ConnectionHops) != 1 { + return "", errorsmod.Wrap(channeltypes.ErrTooManyConnectionHops, "must have direct connection to consumer chain") + } + connectionID := channel.ConnectionHops[0] + _, tmClient, err := k.getUnderlyingClient(ctx, connectionID) + if err != nil { + return "", err + } + + chainID := tmClient.ChainId + if _, ok := k.GetChainToChannel(ctx, chainID); !ok { + return "", errorsmod.Wrapf(types.ErrUnknownConsumerChannelId, "no CCV channel found for chain with ID: %s", chainID) + } + + return chainID, nil +} + +// HandleSetConsumerCommissionRate sets a per-consumer chain commission rate for the given provider address +// on the condition that the given consumer chain exists. +func (k Keeper) HandleSetConsumerCommissionRate(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress, commissionRate sdk.Dec) error { + // check that the consumer chain exists + if !k.IsConsumerProposedOrRegistered(ctx, chainID) { + return errorsmod.Wrapf( + types.ErrUnknownConsumerChainId, + "unknown consumer chain, with id: %s", chainID) + } + + // validate against the minimum commission rate + minRate := k.stakingKeeper.MinCommissionRate(ctx) + if commissionRate.LT(minRate) { + return errorsmod.Wrapf( + stakingtypes.ErrCommissionLTMinRate, + "commission rate cannot be less than %s", minRate, + ) } + // set per-consumer chain commission rate for the validator address + return k.SetConsumerCommissionRate( + ctx, + chainID, + providerAddr, + commissionRate, + ) } diff --git a/x/ccv/provider/keeper/distribution_test.go b/x/ccv/provider/keeper/distribution_test.go new file mode 100644 index 0000000000..4a5cd750fc --- /dev/null +++ b/x/ccv/provider/keeper/distribution_test.go @@ -0,0 +1,272 @@ +package keeper_test + +import ( + "testing" + + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + + tmtypes "github.com/cometbft/cometbft/types" + + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" +) + +func TestComputeConsumerTotalVotingPower(t *testing.T) { + keeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + createVal := func(power int64) tmtypes.Validator { + signer := tmtypes.NewMockPV() + val := tmtypes.NewValidator(signer.PrivKey.PubKey(), power) + return *val + } + + chainID := "consumer" + expTotalPower := int64(0) + + // verify that the total power returned is equal to zero + // when the consumer doesn't exist or has no validators. + require.Zero(t, keeper.ComputeConsumerTotalVotingPower( + ctx, + chainID, + )) + + // set 5 validators to the consumer chain + for i := 0; i < 5; i++ { + val := createVal(int64(i)) + keeper.SetConsumerValidator( + ctx, + chainID, + types.ConsumerValidator{ + ProviderConsAddr: val.Address, + Power: val.VotingPower, + }, + ) + + expTotalPower += val.VotingPower + } + + // compute the total power of opted-in validators + res := keeper.ComputeConsumerTotalVotingPower( + ctx, + chainID, + ) + + // check the total power returned + require.Equal(t, expTotalPower, res) +} + +func TestIdentifyConsumerChainIDFromIBCPacket(t *testing.T) { + var ( + chainID = "consumer" + ccvChannel = "channel-0" + ) + + testCases := []struct { + name string + packet channeltypes.Packet + expectedCalls func(sdk.Context, testkeeper.MockedKeepers, channeltypes.Packet) []*gomock.Call + expCCVChannel bool + expErr bool + }{ + { + "channel not found", + channeltypes.NewPacket( + []byte{}, + 0, + "srcPort", + "srcChannel", + "dstPort", + "dstChannel", + clienttypes.NewHeight(1, 1), + 0, + ), + func(ctx sdk.Context, mocks testkeeper.MockedKeepers, packet channeltypes.Packet) []*gomock.Call { + return []*gomock.Call{ + mocks.MockChannelKeeper.EXPECT().GetChannel( + ctx, + packet.DestinationPort, + packet.DestinationChannel, + ).Return(channeltypes.Channel{}, false).Times(1), + } + }, + false, + true, + }, + { + "connection hops can't be greater than 1", + channeltypes.NewPacket( + []byte{}, + 0, + "srcPort", + "srcChannel", + "dstPort", + "dstChannel", + clienttypes.NewHeight(1, 1), + 0, + ), + func(ctx sdk.Context, mocks testkeeper.MockedKeepers, packet channeltypes.Packet) []*gomock.Call { + return []*gomock.Call{ + mocks.MockChannelKeeper.EXPECT().GetChannel( + ctx, + packet.DestinationPort, + packet.DestinationChannel, + ).Return(channeltypes.Channel{ConnectionHops: []string{"conn1", "conn2"}}, true).Times(1), + } + }, + false, + true, + }, + { + "underlying client isn't found", + channeltypes.NewPacket( + []byte{}, + 0, + "srcPort", + "srcChannel", + "dstPort", + "dstChannel", + clienttypes.NewHeight(1, 1), + 0, + ), + func(ctx sdk.Context, mocks testkeeper.MockedKeepers, packet channeltypes.Packet) []*gomock.Call { + return []*gomock.Call{ + mocks.MockChannelKeeper.EXPECT().GetChannel( + ctx, + packet.DestinationPort, + packet.DestinationChannel, + ).Return(channeltypes.Channel{ConnectionHops: []string{"connectionID"}}, true).Times(1), + mocks.MockConnectionKeeper.EXPECT().GetConnection(ctx, "connectionID").Return( + conntypes.ConnectionEnd{ClientId: "clientID"}, true, + ).Times(1), + mocks.MockClientKeeper.EXPECT().GetClientState(ctx, "clientID").Return( + &ibctmtypes.ClientState{ChainId: ""}, false, + ).Times(1), + } + }, + false, + true, + }, + { + "no CCV channel registered", + channeltypes.NewPacket( + []byte{}, + 0, + "srcPort", + "srcChannel", + "dstPort", + "dstChannel", + clienttypes.NewHeight(1, 1), + 0, + ), + func(ctx sdk.Context, mocks testkeeper.MockedKeepers, packet channeltypes.Packet) []*gomock.Call { + return []*gomock.Call{ + mocks.MockChannelKeeper.EXPECT().GetChannel( + ctx, + packet.DestinationPort, + packet.DestinationChannel, + ).Return(channeltypes.Channel{ConnectionHops: []string{"connectionID"}}, true).Times(1), + mocks.MockConnectionKeeper.EXPECT().GetConnection(ctx, "connectionID").Return( + conntypes.ConnectionEnd{ClientId: "clientID"}, true, + ).Times(1), + mocks.MockClientKeeper.EXPECT().GetClientState(ctx, "clientID").Return( + &ibctmtypes.ClientState{ChainId: chainID}, true, + ).Times(1), + } + }, + false, + true, + }, + { + "consumer chain identified", + channeltypes.NewPacket( + []byte{}, + 0, + "srcPort", + "srcChannel", + "dstPort", + "dstChannel", + clienttypes.NewHeight(1, 1), + 0, + ), + func(ctx sdk.Context, mocks testkeeper.MockedKeepers, packet channeltypes.Packet) []*gomock.Call { + return []*gomock.Call{ + mocks.MockChannelKeeper.EXPECT().GetChannel( + ctx, + packet.DestinationPort, + packet.DestinationChannel, + ), + } + }, + false, + true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + keeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + tc.expectedCalls(ctx, mocks, tc.packet) + _, err := keeper.IdentifyConsumerChainIDFromIBCPacket( + ctx, + tc.packet, + ) + + if tc.expCCVChannel { + keeper.SetChainToChannel(ctx, chainID, ccvChannel) + } + + if !tc.expErr { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} + +func TestSetConsumerRewardsAllocation(t *testing.T) { + keeperParams := testkeeper.NewInMemKeeperParams(t) + ctx := keeperParams.Ctx + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mocks := testkeeper.NewMockedKeepers(ctrl) + providerKeeper := testkeeper.NewInMemProviderKeeper(keeperParams, mocks) + + rewardAllocation := providertypes.ConsumerRewardsAllocation{ + Rewards: sdk.NewDecCoins(sdk.NewDecCoin("uatom", sdk.NewInt(1000))), + } + + providerKeeper.SetConsumerRewardsAllocation(ctx, "consumer-1", rewardAllocation) + + alloc := providerKeeper.GetConsumerRewardsAllocation(ctx, "consumer-1") + require.Equal(t, rewardAllocation, alloc) +} + +func TestGetConsumerRewardsAllocationNil(t *testing.T) { + keeperParams := testkeeper.NewInMemKeeperParams(t) + ctx := keeperParams.Ctx + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mocks := testkeeper.NewMockedKeepers(ctrl) + providerKeeper := testkeeper.NewInMemProviderKeeper(keeperParams, mocks) + + alloc := providerKeeper.GetConsumerRewardsAllocation(ctx, "consumer-1") + + expectedRewardAllocation := providertypes.ConsumerRewardsAllocation{ + Rewards: nil, + } + require.Equal(t, expectedRewardAllocation, alloc) +} diff --git a/x/ccv/provider/keeper/genesis.go b/x/ccv/provider/keeper/genesis.go index 2075ff48ae..66895233a7 100644 --- a/x/ccv/provider/keeper/genesis.go +++ b/x/ccv/provider/keeper/genesis.go @@ -5,8 +5,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // InitGenesis initializes the CCV provider state and binds to PortID. diff --git a/x/ccv/provider/keeper/genesis_test.go b/x/ccv/provider/keeper/genesis_test.go index 81b0a90bd8..e75241eb58 100644 --- a/x/ccv/provider/keeper/genesis_test.go +++ b/x/ccv/provider/keeper/genesis_test.go @@ -11,11 +11,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v4/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // TestInitAndExportGenesis tests the export and the initialisation of a provider chain genesis diff --git a/x/ccv/provider/keeper/grpc_query.go b/x/ccv/provider/keeper/grpc_query.go index 6803c60ddd..2baed949b7 100644 --- a/x/ccv/provider/keeper/grpc_query.go +++ b/x/ccv/provider/keeper/grpc_query.go @@ -2,6 +2,7 @@ package keeper import ( "context" + "fmt" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -10,8 +11,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) var _ types.QueryServer = Keeper{} @@ -45,7 +46,6 @@ func (k Keeper) QueryConsumerChains(goCtx context.Context, req *types.QueryConsu ctx := sdk.UnwrapSDKContext(goCtx) - // convert to array of pointers chains := []*types.Chain{} for _, chain := range k.GetAllConsumerChains(ctx) { // prevent implicit memory aliasing @@ -225,6 +225,110 @@ func (k Keeper) QueryParams(c context.Context, _ *types.QueryParamsRequest) (*ty return &types.QueryParamsResponse{Params: params}, nil } +// QueryConsumerChainOptedInValidators returns all validators that opted-in to a given consumer chain +func (k Keeper) QueryConsumerChainOptedInValidators(goCtx context.Context, req *types.QueryConsumerChainOptedInValidatorsRequest) (*types.QueryConsumerChainOptedInValidatorsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + consumerChainID := req.ChainId + if consumerChainID == "" { + return nil, status.Error(codes.InvalidArgument, "empty chainId") + } + + optedInVals := []string{} + ctx := sdk.UnwrapSDKContext(goCtx) + + if !k.IsConsumerProposedOrRegistered(ctx, consumerChainID) { + return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("unknown consumer chain: %s", consumerChainID)) + } + + for _, v := range k.GetAllOptedIn(ctx, consumerChainID) { + optedInVals = append(optedInVals, v.ToSdkConsAddr().String()) + } + + return &types.QueryConsumerChainOptedInValidatorsResponse{ + ValidatorsProviderAddresses: optedInVals, + }, nil +} + +// QueryConsumerChainsValidatorHasToValidate returns all consumer chains that the given validator has to validate now +// or in the next epoch if nothing changes. +func (k Keeper) QueryConsumerChainsValidatorHasToValidate(goCtx context.Context, req *types.QueryConsumerChainsValidatorHasToValidateRequest) (*types.QueryConsumerChainsValidatorHasToValidateResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if req.ProviderAddress == "" { + return nil, status.Error(codes.InvalidArgument, "empty provider address") + } + + consAddr, err := sdk.ConsAddressFromBech32(req.ProviderAddress) + if err != nil { + return nil, status.Error(codes.InvalidArgument, "invalid provider address") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + provAddr := types.NewProviderConsAddress(consAddr) + + // get all the consumer chains for which the validator is either already + // opted-in, currently a consumer validator or if its voting power is within the TopN validators + consumersToValidate := []string{} + for _, consumer := range k.GetAllConsumerChains(ctx) { + chainID := consumer.ChainId + + if hasToValidate, err := k.HasToValidate(ctx, provAddr, chainID); err == nil && hasToValidate { + consumersToValidate = append(consumersToValidate, chainID) + } + } + + return &types.QueryConsumerChainsValidatorHasToValidateResponse{ + ConsumerChainIds: consumersToValidate, + }, nil +} + +// QueryValidatorConsumerCommissionRate returns the commission rate a given +// validator charges on a given consumer chain +func (k Keeper) QueryValidatorConsumerCommissionRate(goCtx context.Context, req *types.QueryValidatorConsumerCommissionRateRequest) (*types.QueryValidatorConsumerCommissionRateResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + consumerChainID := req.ChainId + if consumerChainID == "" { + return nil, status.Error(codes.InvalidArgument, "empty chainId") + } + + consAddr, err := sdk.ConsAddressFromBech32(req.ProviderAddress) + if err != nil { + return nil, status.Error(codes.InvalidArgument, "invalid provider address") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + if !k.IsConsumerProposedOrRegistered(ctx, consumerChainID) { + return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("unknown consumer chain: %s", consumerChainID)) + } + + res := &types.QueryValidatorConsumerCommissionRateResponse{} + + // Check if the validator has a commission rate set for the consumer chain, + // otherwise use the commission rate from the validator staking module struct + consumerRate, found := k.GetConsumerCommissionRate(ctx, consumerChainID, types.NewProviderConsAddress(consAddr)) + if found { + res.Rate = consumerRate + } else { + v, ok := k.stakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + if !ok { + return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("unknown validator: %s", consAddr.String())) + } + res.Rate = v.Commission.Rate + } + + return res, nil +} + func (k Keeper) QueryOldestUnconfirmedVsc(goCtx context.Context, req *types.QueryOldestUnconfirmedVscRequest) (*types.QueryOldestUnconfirmedVscResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) diff --git a/x/ccv/provider/keeper/grpc_query_test.go b/x/ccv/provider/keeper/grpc_query_test.go index 14e6e675e1..8283f11235 100644 --- a/x/ccv/provider/keeper/grpc_query_test.go +++ b/x/ccv/provider/keeper/grpc_query_test.go @@ -8,10 +8,10 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" - cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) func TestQueryAllPairsValConAddrByConsumerChainID(t *testing.T) { diff --git a/x/ccv/provider/keeper/hooks.go b/x/ccv/provider/keeper/hooks.go index 88590a9875..763804868e 100644 --- a/x/ccv/provider/keeper/hooks.go +++ b/x/ccv/provider/keeper/hooks.go @@ -8,8 +8,9 @@ import ( v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // Wrapper struct @@ -35,8 +36,64 @@ func (k *Keeper) Hooks() Hooks { func (h Hooks) AfterUnbondingInitiated(ctx sdk.Context, id uint64) error { var consumerChainIDS []string + // get validator address from unbonding operation + unbondingType, found := h.k.stakingKeeper.GetUnbondingType(ctx, id) + vadAddrBech32 := "" + if !found { + ctx.Logger().Error("undefined type for unbonding operation id: %d", id) + return nil + } + + switch unbondingType { + case stakingtypes.UnbondingType_UnbondingDelegation: + ubd, found := h.k.stakingKeeper.GetUnbondingDelegationByUnbondingID(ctx, id) + if !found { + ctx.Logger().Error("unfound ubonding delegation for unbonding id: %d", id) + return nil + } + vadAddrBech32 = ubd.ValidatorAddress + case stakingtypes.UnbondingType_Redelegation: + red, found := h.k.stakingKeeper.GetRedelegationByUnbondingID(ctx, id) + if !found { + ctx.Logger().Error("unfound relegation for unbonding operation id: %d", id) + return nil + } + vadAddrBech32 = red.ValidatorSrcAddress + case stakingtypes.UnbondingType_ValidatorUnbonding: + val, found := h.k.stakingKeeper.GetValidatorByUnbondingID(ctx, id) + if !found { + ctx.Logger().Error("unfound validator for unbonding operation id: %d", id) + return nil + } + vadAddrBech32 = val.OperatorAddress + default: + ctx.Logger().Error("invalid unbonding operation type: %s", unbondingType) + return nil + } + + valAddr, err := sdk.ValAddressFromBech32(vadAddrBech32) + if err != nil { + ctx.Logger().Error(err.Error()) + return nil + } + + validator, found := h.k.stakingKeeper.GetValidator(ctx, valAddr) + if !found { + ctx.Logger().Error("unfound validator for validator address %s", vadAddrBech32) + return nil + } + + consAddr, err := validator.GetConsAddr() + if err != nil { + ctx.Logger().Error(err.Error()) + return nil + } + + // get all consumers where the validator is in the validator set for _, chain := range h.k.GetAllConsumerChains(ctx) { - consumerChainIDS = append(consumerChainIDS, chain.ChainId) + if h.k.IsConsumerValidator(ctx, chain.ChainId, types.NewProviderConsAddress(consAddr)) { + consumerChainIDS = append(consumerChainIDS, chain.ChainId) + } } if len(consumerChainIDS) == 0 { diff --git a/x/ccv/provider/keeper/hooks_test.go b/x/ccv/provider/keeper/hooks_test.go index f4aad8d441..5372fc37cd 100644 --- a/x/ccv/provider/keeper/hooks_test.go +++ b/x/ccv/provider/keeper/hooks_test.go @@ -15,9 +15,9 @@ import ( v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" ) func TestValidatorConsensusKeyInUse(t *testing.T) { @@ -123,11 +123,13 @@ func TestAfterPropSubmissionAndVotingPeriodEnded(t *testing.T) { k.Hooks().AfterProposalSubmission(ctx, prop.Id) // verify that the proposal ID is created - require.NotEmpty(t, k.GetProposedConsumerChain(ctx, prop.Id)) + _, found := k.GetProposedConsumerChain(ctx, prop.Id) + require.True(t, found) k.Hooks().AfterProposalVotingPeriodEnded(ctx, prop.Id) // verify that the proposal ID is deleted - require.Empty(t, k.GetProposedConsumerChain(ctx, prop.Id)) + _, found = k.GetProposedConsumerChain(ctx, prop.Id) + require.False(t, found) } func TestGetConsumerAdditionLegacyPropFromProp(t *testing.T) { diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 510d957c5d..e602c16e5e 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -23,9 +23,9 @@ import ( "github.com/cometbft/cometbft/libs/log" - consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // Keeper defines the Cross-Chain Validation Provider Keeper @@ -186,9 +186,14 @@ func (k Keeper) SetProposedConsumerChain(ctx sdk.Context, chainID string, propos } // GetProposedConsumerChain returns the proposed chainID for the given consumerAddition proposal ID. -func (k Keeper) GetProposedConsumerChain(ctx sdk.Context, proposalID uint64) string { +// This method is only used for testing. +func (k Keeper) GetProposedConsumerChain(ctx sdk.Context, proposalID uint64) (string, bool) { store := ctx.KVStore(k.storeKey) - return string(store.Get(types.ProposedConsumerChainKey(proposalID))) + consumerChain := store.Get(types.ProposedConsumerChainKey(proposalID)) + if consumerChain != nil { + return string(consumerChain), true + } + return "", false } // DeleteProposedConsumerChainInStore deletes the consumer chainID from store @@ -248,9 +253,12 @@ func (k Keeper) GetAllConsumerChains(ctx sdk.Context) (chains []types.Chain) { chainID := string(iterator.Key()[1:]) clientID := string(iterator.Value()) + topN, _ := k.GetTopN(ctx, chainID) + chains = append(chains, types.Chain{ ChainId: chainID, ClientId: clientID, + Top_N: topN, }) } @@ -1136,3 +1144,394 @@ func (k Keeper) GetAllRegisteredAndProposedChainIDs(ctx sdk.Context) []string { return allConsumerChains } + +// SetTopN stores the N value associated to chain with `chainID` +func (k Keeper) SetTopN( + ctx sdk.Context, + chainID string, + N uint32, +) { + store := ctx.KVStore(k.storeKey) + + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, N) + + store.Set(types.TopNKey(chainID), buf) +} + +// DeleteTopN removes the N value associated to chain with `chainID` +func (k Keeper) DeleteTopN( + ctx sdk.Context, + chainID string, +) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.TopNKey(chainID)) +} + +// GetTopN returns (N, true) if chain `chainID` has a top N associated, and (0, false) otherwise. +func (k Keeper) GetTopN( + ctx sdk.Context, + chainID string, +) (uint32, bool) { + store := ctx.KVStore(k.storeKey) + buf := store.Get(types.TopNKey(chainID)) + if buf == nil { + return 0, false + } + return binary.BigEndian.Uint32(buf), true +} + +// IsTopN returns true if chain with `chainID` is a Top-N chain (i.e., enforces at least one validator to validate chain `chainID`) +func (k Keeper) IsTopN(ctx sdk.Context, chainID string) bool { + topN, found := k.GetTopN(ctx, chainID) + return found && topN > 0 +} + +// IsOptIn returns true if chain with `chainID` is an Opt-In chain (i.e., no validator is forced to validate chain `chainID`) +func (k Keeper) IsOptIn(ctx sdk.Context, chainID string) bool { + topN, found := k.GetTopN(ctx, chainID) + return !found || topN == 0 +} + +func (k Keeper) SetOptedIn( + ctx sdk.Context, + chainID string, + providerConsAddress types.ProviderConsAddress, +) { + store := ctx.KVStore(k.storeKey) + store.Set(types.OptedInKey(chainID, providerConsAddress), []byte{}) +} + +func (k Keeper) DeleteOptedIn( + ctx sdk.Context, + chainID string, + providerAddr types.ProviderConsAddress, +) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.OptedInKey(chainID, providerAddr)) +} + +func (k Keeper) IsOptedIn( + ctx sdk.Context, + chainID string, + providerAddr types.ProviderConsAddress, +) bool { + store := ctx.KVStore(k.storeKey) + return store.Get(types.OptedInKey(chainID, providerAddr)) != nil +} + +// GetAllOptedIn returns all the opted-in validators on chain `chainID` +func (k Keeper) GetAllOptedIn( + ctx sdk.Context, + chainID string, +) (providerConsAddresses []types.ProviderConsAddress) { + store := ctx.KVStore(k.storeKey) + key := types.ChainIdWithLenKey(types.OptedInBytePrefix, chainID) + iterator := sdk.KVStorePrefixIterator(store, key) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + providerConsAddresses = append(providerConsAddresses, types.NewProviderConsAddress(iterator.Key()[len(key):])) + } + + return providerConsAddresses +} + +// DeleteAllOptedIn deletes all the opted-in validators for chain with `chainID` +func (k Keeper) DeleteAllOptedIn( + ctx sdk.Context, + chainID string, +) { + store := ctx.KVStore(k.storeKey) + key := types.ChainIdWithLenKey(types.OptedInBytePrefix, chainID) + iterator := sdk.KVStorePrefixIterator(store, key) + + var keysToDel [][]byte + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + keysToDel = append(keysToDel, iterator.Key()) + } + for _, delKey := range keysToDel { + store.Delete(delKey) + } +} + +func (k Keeper) HasToValidate( + ctx sdk.Context, + provAddr types.ProviderConsAddress, + chainID string, +) (bool, error) { + // if the validator was sent as part of the packet in the last epoch, it has to validate + if k.IsConsumerValidator(ctx, chainID, provAddr) { + return true, nil + } + + // if the validator was not part of the last epoch, check if the validator is going to be part of te next epoch + bondedValidators := k.stakingKeeper.GetLastValidators(ctx) + if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { + // in a Top-N chain, we automatically opt in all validators that belong to the top N + minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, bondedValidators, topN) + if err == nil { + k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) + } + } + + // if the validator is opted in and belongs to the validators of the next epoch, then if nothing changes + // the validator would have to validate in the next epoch + if k.IsOptedIn(ctx, chainID, provAddr) { + nextValidators := k.ComputeNextValidators(ctx, chainID, k.stakingKeeper.GetLastValidators(ctx)) + for _, v := range nextValidators { + consAddr := sdk.ConsAddress(v.ProviderConsAddr) + if provAddr.ToSdkConsAddr().Equals(consAddr) { + return true, nil + } + } + } + + return false, nil +} + +// SetConsumerCommissionRate sets a per-consumer chain commission rate +// for the given validator address +func (k Keeper) SetConsumerCommissionRate( + ctx sdk.Context, + chainID string, + providerAddr types.ProviderConsAddress, + commissionRate sdk.Dec, +) error { + store := ctx.KVStore(k.storeKey) + bz, err := commissionRate.Marshal() + if err != nil { + err = fmt.Errorf("consumer commission rate marshalling failed: %s", err) + k.Logger(ctx).Error(err.Error()) + return err + } + + store.Set(types.ConsumerCommissionRateKey(chainID, providerAddr), bz) + return nil +} + +// GetConsumerCommissionRate returns the per-consumer commission rate set +// for the given validator address +func (k Keeper) GetConsumerCommissionRate( + ctx sdk.Context, + chainID string, + providerAddr types.ProviderConsAddress, +) (sdk.Dec, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.ConsumerCommissionRateKey(chainID, providerAddr)) + if bz == nil { + return sdk.ZeroDec(), false + } + + cr := sdk.Dec{} + // handle error gracefully since it's called in BeginBlockRD + if err := cr.Unmarshal(bz); err != nil { + k.Logger(ctx).Error("consumer commission rate unmarshalling failed: %s", err) + return sdk.ZeroDec(), false + } + + return cr, true +} + +// GetAllCommissionRateValidators returns all the validator address +// that set a commission rate for the given chain ID +func (k Keeper) GetAllCommissionRateValidators( + ctx sdk.Context, + chainID string, +) (addresses []types.ProviderConsAddress) { + store := ctx.KVStore(k.storeKey) + key := types.ChainIdWithLenKey(types.ConsumerCommissionRatePrefix, chainID) + iterator := sdk.KVStorePrefixIterator(store, key) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + providerAddr := types.NewProviderConsAddress(iterator.Key()[len(key):]) + addresses = append(addresses, providerAddr) + } + + return addresses +} + +// DeleteConsumerCommissionRate the per-consumer chain commission rate +// associated to the given validator address +func (k Keeper) DeleteConsumerCommissionRate( + ctx sdk.Context, + chainID string, + providerAddr types.ProviderConsAddress, +) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.ConsumerCommissionRateKey(chainID, providerAddr)) +} + +// SetValidatorsPowerCap sets the power-cap value `p` associated to chain with `chainID` +func (k Keeper) SetValidatorsPowerCap( + ctx sdk.Context, + chainID string, + p uint32, +) { + store := ctx.KVStore(k.storeKey) + + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, p) + + store.Set(types.ValidatorsPowerCapKey(chainID), buf) +} + +// DeleteValidatorsPowerCap removes the power-cap value associated to chain with `chainID` +func (k Keeper) DeleteValidatorsPowerCap( + ctx sdk.Context, + chainID string, +) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.ValidatorsPowerCapKey(chainID)) +} + +// GetValidatorsPowerCap returns `(p, true)` if chain `chainID` has power cap `p` associated with it, and (0, false) otherwise +func (k Keeper) GetValidatorsPowerCap( + ctx sdk.Context, + chainID string, +) (uint32, bool) { + store := ctx.KVStore(k.storeKey) + buf := store.Get(types.ValidatorsPowerCapKey(chainID)) + if buf == nil { + return 0, false + } + return binary.BigEndian.Uint32(buf), true +} + +// SetValidatorSetCap stores the validator-set cap value `c` associated to chain with `chainID` +func (k Keeper) SetValidatorSetCap( + ctx sdk.Context, + chainID string, + c uint32, +) { + store := ctx.KVStore(k.storeKey) + + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, c) + + store.Set(types.ValidatorSetCapKey(chainID), buf) +} + +// DeleteValidatorSetCap removes the validator-set cap value associated to chain with `chainID` +func (k Keeper) DeleteValidatorSetCap( + ctx sdk.Context, + chainID string, +) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.ValidatorSetCapKey(chainID)) +} + +// GetValidatorSetCap returns `(c, true)` if chain `chainID` has validator-set cap `c` associated with it, and (0, false) otherwise +func (k Keeper) GetValidatorSetCap( + ctx sdk.Context, + chainID string, +) (uint32, bool) { + store := ctx.KVStore(k.storeKey) + buf := store.Get(types.ValidatorSetCapKey(chainID)) + if buf == nil { + return 0, false + } + return binary.BigEndian.Uint32(buf), true +} + +// SetAllowlist allowlists validator with `providerAddr` address on chain `chainID` +func (k Keeper) SetAllowlist( + ctx sdk.Context, + chainID string, + providerAddr types.ProviderConsAddress, +) { + store := ctx.KVStore(k.storeKey) + store.Set(types.AllowlistCapKey(chainID, providerAddr), []byte{}) +} + +// IsAllowlisted returns `true` if validator with `providerAddr` has been allowlisted on chain `chainID` +func (k Keeper) IsAllowlisted( + ctx sdk.Context, + chainID string, + providerAddr types.ProviderConsAddress, +) bool { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.AllowlistCapKey(chainID, providerAddr)) + return bz != nil +} + +// DeleteAllowlist deletes all allowlisted validators +func (k Keeper) DeleteAllowlist(ctx sdk.Context, chainID string) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.AllowlistPrefix, chainID)) + defer iterator.Close() + + keysToDel := [][]byte{} + for ; iterator.Valid(); iterator.Next() { + keysToDel = append(keysToDel, iterator.Key()) + } + + for _, key := range keysToDel { + store.Delete(key) + } +} + +// IsAllowlistEmpty returns `true` if no validator is allowlisted on chain `chainID` +func (k Keeper) IsAllowlistEmpty(ctx sdk.Context, chainID string) bool { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.AllowlistPrefix, chainID)) + defer iterator.Close() + + if iterator.Valid() { + return false + } + + return true +} + +// SetDenylist denylists validator with `providerAddr` address on chain `chainID` +func (k Keeper) SetDenylist( + ctx sdk.Context, + chainID string, + providerAddr types.ProviderConsAddress, +) { + store := ctx.KVStore(k.storeKey) + store.Set(types.DenylistCapKey(chainID, providerAddr), []byte{}) +} + +// IsDenylisted returns `true` if validator with `providerAddr` has been denylisted on chain `chainID` +func (k Keeper) IsDenylisted( + ctx sdk.Context, + chainID string, + providerAddr types.ProviderConsAddress, +) bool { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.DenylistCapKey(chainID, providerAddr)) + return bz != nil +} + +// DeleteDenylist deletes all denylisted validators +func (k Keeper) DeleteDenylist(ctx sdk.Context, chainID string) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.DenylistPrefix, chainID)) + defer iterator.Close() + + keysToDel := [][]byte{} + for ; iterator.Valid(); iterator.Next() { + keysToDel = append(keysToDel, iterator.Key()) + } + + for _, key := range keysToDel { + store.Delete(key) + } +} + +// IsDenylistEmpty returns `true` if no validator is denylisted on chain `chainID` +func (k Keeper) IsDenylistEmpty(ctx sdk.Context, chainID string) bool { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.DenylistPrefix, chainID)) + defer iterator.Close() + + if iterator.Valid() { + return false + } + + return true +} diff --git a/x/ccv/provider/keeper/keeper_test.go b/x/ccv/provider/keeper/keeper_test.go index 9c9d421fd9..a66b7e3826 100644 --- a/x/ccv/provider/keeper/keeper_test.go +++ b/x/ccv/provider/keeper/keeper_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "bytes" "fmt" "sort" "testing" @@ -10,14 +11,15 @@ import ( "github.com/stretchr/testify/require" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" abci "github.com/cometbft/cometbft/abci/types" tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) const consumer = "consumer" @@ -400,8 +402,10 @@ func TestGetAllConsumerChains(t *testing.T) { expectedGetAllOrder := []types.Chain{} for i, chainID := range chainIDs { clientID := fmt.Sprintf("client-%d", len(chainIDs)-i) + topN := uint32(i) pk.SetConsumerClientId(ctx, chainID, clientID) - expectedGetAllOrder = append(expectedGetAllOrder, types.Chain{ChainId: chainID, ClientId: clientID}) + pk.SetTopN(ctx, chainID, topN) + expectedGetAllOrder = append(expectedGetAllOrder, types.Chain{ChainId: chainID, ClientId: clientID, Top_N: topN}) } // sorting by chainID sort.Slice(expectedGetAllOrder, func(i, j int) bool { @@ -550,7 +554,7 @@ func TestSetProposedConsumerChains(t *testing.T) { for _, test := range tests { providerKeeper.SetProposedConsumerChain(ctx, test.chainID, test.proposalID) - cID := providerKeeper.GetProposedConsumerChain(ctx, test.proposalID) + cID, _ := providerKeeper.GetProposedConsumerChain(ctx, test.proposalID) require.Equal(t, cID, test.chainID) } } @@ -572,9 +576,9 @@ func TestDeleteProposedConsumerChainInStore(t *testing.T) { for _, test := range tests { providerKeeper.SetProposedConsumerChain(ctx, test.chainID, test.proposalID) providerKeeper.DeleteProposedConsumerChainInStore(ctx, test.deleteProposalID) - cID := providerKeeper.GetProposedConsumerChain(ctx, test.proposalID) + cID, found := providerKeeper.GetProposedConsumerChain(ctx, test.proposalID) if test.ok { - require.Equal(t, cID, "") + require.False(t, found) } else { require.Equal(t, cID, test.chainID) } @@ -628,3 +632,208 @@ func TestGetAllProposedConsumerChainIDs(t *testing.T) { } } } + +// TestTopN tests the `SetTopN`, `GetTopN`, `IsTopN`, and `IsOptIn` methods +func TestTopN(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + tests := []struct { + chainID string + N uint32 + isOptIn bool + }{ + {chainID: "TopNChain1", N: 50, isOptIn: false}, + {chainID: "TopNChain2", N: 100, isOptIn: false}, + {chainID: "OptInChain", N: 0, isOptIn: true}, + } + + for _, test := range tests { + providerKeeper.SetTopN(ctx, test.chainID, test.N) + topN, found := providerKeeper.GetTopN(ctx, test.chainID) + require.Equal(t, test.N, topN) + require.True(t, found) + + if test.isOptIn { + require.True(t, providerKeeper.IsOptIn(ctx, test.chainID)) + require.False(t, providerKeeper.IsTopN(ctx, test.chainID)) + } else { + require.False(t, providerKeeper.IsOptIn(ctx, test.chainID)) + require.True(t, providerKeeper.IsTopN(ctx, test.chainID)) + } + } +} + +func TestGetAllOptedIn(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + expectedOptedInValidators := []types.ProviderConsAddress{ + types.NewProviderConsAddress([]byte("providerAddr1")), + types.NewProviderConsAddress([]byte("providerAddr2")), + types.NewProviderConsAddress([]byte("providerAddr3")), + } + + for _, expectedOptedInValidator := range expectedOptedInValidators { + providerKeeper.SetOptedIn(ctx, "chainID", expectedOptedInValidator) + } + + actualOptedInValidators := providerKeeper.GetAllOptedIn(ctx, "chainID") + + // sort validators first to be able to compare + sortOptedInValidators := func(addresses []types.ProviderConsAddress) { + sort.Slice(addresses, func(i, j int) bool { + return bytes.Compare(addresses[i].ToSdkConsAddr(), addresses[j].ToSdkConsAddr()) < 0 + }) + } + sortOptedInValidators(expectedOptedInValidators) + sortOptedInValidators(actualOptedInValidators) + require.Equal(t, expectedOptedInValidators, actualOptedInValidators) +} + +// TestOptedIn tests the `SetOptedIn`, `IsOptedIn`, `DeleteOptedIn` and `DeleteAllOptedIn` methods +func TestOptedIn(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + optedInValidator1 := types.NewProviderConsAddress([]byte("providerAddr1")) + optedInValidator2 := types.NewProviderConsAddress([]byte("providerAddr2")) + + require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", optedInValidator1)) + providerKeeper.SetOptedIn(ctx, "chainID", optedInValidator1) + require.True(t, providerKeeper.IsOptedIn(ctx, "chainID", optedInValidator1)) + providerKeeper.DeleteOptedIn(ctx, "chainID", optedInValidator1) + require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", optedInValidator1)) + + providerKeeper.SetOptedIn(ctx, "chainID", optedInValidator1) + providerKeeper.SetOptedIn(ctx, "chainID", optedInValidator2) + require.True(t, providerKeeper.IsOptedIn(ctx, "chainID", optedInValidator1)) + require.True(t, providerKeeper.IsOptedIn(ctx, "chainID", optedInValidator2)) + providerKeeper.DeleteAllOptedIn(ctx, "chainID") + require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", optedInValidator1)) + require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", optedInValidator2)) +} + +// TestConsumerCommissionRate tests the `SetConsumerCommissionRate`, `GetConsumerCommissionRate`, and `DeleteConsumerCommissionRate` methods +func TestConsumerCommissionRate(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + providerAddr1 := types.NewProviderConsAddress([]byte("providerAddr1")) + providerAddr2 := types.NewProviderConsAddress([]byte("providerAddr2")) + + cr, found := providerKeeper.GetConsumerCommissionRate(ctx, "chainID", providerAddr1) + require.False(t, found) + require.Equal(t, sdk.ZeroDec(), cr) + + providerKeeper.SetConsumerCommissionRate(ctx, "chainID", providerAddr1, sdk.OneDec()) + cr, found = providerKeeper.GetConsumerCommissionRate(ctx, "chainID", providerAddr1) + require.True(t, found) + require.Equal(t, sdk.OneDec(), cr) + + providerKeeper.SetConsumerCommissionRate(ctx, "chainID", providerAddr2, sdk.ZeroDec()) + cr, found = providerKeeper.GetConsumerCommissionRate(ctx, "chainID", providerAddr2) + require.True(t, found) + require.Equal(t, sdk.ZeroDec(), cr) + + provAddrs := providerKeeper.GetAllCommissionRateValidators(ctx, "chainID") + require.Len(t, provAddrs, 2) + + for _, addr := range provAddrs { + providerKeeper.DeleteConsumerCommissionRate(ctx, "chainID", addr) + } + + _, found = providerKeeper.GetConsumerCommissionRate(ctx, "chainID", providerAddr1) + require.False(t, found) + + _, found = providerKeeper.GetConsumerCommissionRate(ctx, "chainID", providerAddr2) + require.False(t, found) +} + +// TestValidatorsPowerCap tests the `SetValidatorsPowerCap`, `GetValidatorsPowerCap`, and `DeleteValidatorsPowerCap` methods +func TestValidatorsPowerCap(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + expectedPowerCap := uint32(10) + providerKeeper.SetValidatorsPowerCap(ctx, "chainID", expectedPowerCap) + powerCap, found := providerKeeper.GetValidatorsPowerCap(ctx, "chainID") + require.Equal(t, expectedPowerCap, powerCap) + require.True(t, found) + + providerKeeper.DeleteValidatorsPowerCap(ctx, "chainID") + _, found = providerKeeper.GetValidatorsPowerCap(ctx, "chainID") + require.False(t, found) +} + +// TestValidatorSetCap tests the `SetValidatorSetCap`, `GetValidatorSetCap`, and `DeleteValidatorSetCap` methods +func TestValidatorSetCap(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + expectedPowerCap := uint32(10) + providerKeeper.SetValidatorSetCap(ctx, "chainID", expectedPowerCap) + powerCap, found := providerKeeper.GetValidatorSetCap(ctx, "chainID") + require.Equal(t, expectedPowerCap, powerCap) + require.True(t, found) + + providerKeeper.DeleteValidatorSetCap(ctx, "chainID") + _, found = providerKeeper.GetValidatorSetCap(ctx, "chainID") + require.False(t, found) +} + +// TestAllowlist tests the `SetAllowlist`, `IsAllowlisted`, `DeleteAllowlist`, and `IsAllowlistEmpty` methods +func TestAllowlist(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainID := "chainID" + + // no validator was allowlisted and hence the allowlist is empty + require.True(t, providerKeeper.IsAllowlistEmpty(ctx, chainID)) + + providerAddr1 := types.NewProviderConsAddress([]byte("providerAddr1")) + providerKeeper.SetAllowlist(ctx, chainID, providerAddr1) + require.True(t, providerKeeper.IsAllowlisted(ctx, chainID, providerAddr1)) + + // allowlist is not empty anymore + require.False(t, providerKeeper.IsAllowlistEmpty(ctx, chainID)) + + providerAddr2 := types.NewProviderConsAddress([]byte("providerAddr2")) + providerKeeper.SetAllowlist(ctx, chainID, providerAddr2) + require.True(t, providerKeeper.IsAllowlisted(ctx, chainID, providerAddr2)) + require.False(t, providerKeeper.IsAllowlistEmpty(ctx, chainID)) + + providerKeeper.DeleteAllowlist(ctx, chainID) + require.False(t, providerKeeper.IsAllowlisted(ctx, chainID, providerAddr1)) + require.False(t, providerKeeper.IsAllowlisted(ctx, chainID, providerAddr2)) + require.True(t, providerKeeper.IsAllowlistEmpty(ctx, chainID)) +} + +// TestDenylist tests the `SetDenylist`, `IsDenylisted`, `DeleteDenylist`, and `IsDenylistEmpty` methods +func TestDenylist(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainID := "chainID" + + // no validator was denylisted and hence the denylist is empty + require.True(t, providerKeeper.IsDenylistEmpty(ctx, chainID)) + + providerAddr1 := types.NewProviderConsAddress([]byte("providerAddr1")) + providerKeeper.SetDenylist(ctx, chainID, providerAddr1) + require.True(t, providerKeeper.IsDenylisted(ctx, chainID, providerAddr1)) + + // denylist is not empty anymore + require.False(t, providerKeeper.IsDenylistEmpty(ctx, chainID)) + + providerAddr2 := types.NewProviderConsAddress([]byte("providerAddr2")) + providerKeeper.SetDenylist(ctx, chainID, providerAddr2) + require.True(t, providerKeeper.IsDenylisted(ctx, chainID, providerAddr2)) + require.False(t, providerKeeper.IsDenylistEmpty(ctx, chainID)) + + providerKeeper.DeleteDenylist(ctx, chainID) + require.False(t, providerKeeper.IsDenylisted(ctx, chainID, providerAddr1)) + require.False(t, providerKeeper.IsDenylisted(ctx, chainID, providerAddr2)) + require.True(t, providerKeeper.IsDenylistEmpty(ctx, chainID)) +} diff --git a/x/ccv/provider/keeper/key_assignment.go b/x/ccv/provider/keeper/key_assignment.go index 89dbcfda4e..cb359620b9 100644 --- a/x/ccv/provider/keeper/key_assignment.go +++ b/x/ccv/provider/keeper/key_assignment.go @@ -1,6 +1,7 @@ package keeper import ( + "encoding/base64" "fmt" errorsmod "cosmossdk.io/errors" @@ -10,10 +11,57 @@ import ( tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) +// ParseConsumerKey parses the ED25519 PubKey`consumerKey` from a JSON string +// and constructs its corresponding `tmprotocrypto.PublicKey` +func (k Keeper) ParseConsumerKey(consumerKey string) (tmprotocrypto.PublicKey, error) { + // parse consumer key as long as it's in the right format + pkType, keyStr, err := types.ParseConsumerKeyFromJson(consumerKey) + if err != nil { + return tmprotocrypto.PublicKey{}, err + } + + // Note: the correct way to decide if a key type is supported is to check the + // consensus params. However this functionality was disabled in https://github.com/cosmos/interchain-security/pull/916 + // as a quick way to get ed25519 working, avoiding amino/proto-any marshalling issues. + + // make sure the consumer key type is supported + // cp := ctx.ConsensusParams() + // if cp != nil && cp.Validator != nil { + // if !tmstrings.StringInSlice(pkType, cp.Validator.PubKeyTypes) { + // return nil, errorsmod.Wrapf( + // stakingtypes.ErrValidatorPubKeyTypeNotSupported, + // "got: %s, expected one of: %s", pkType, cp.Validator.PubKeyTypes, + // ) + // } + // } + + // For now, only accept ed25519. + // TODO: decide what types should be supported. + if pkType != "/cosmos.crypto.ed25519.PubKey" { + return tmprotocrypto.PublicKey{}, errorsmod.Wrapf( + stakingtypes.ErrValidatorPubKeyTypeNotSupported, + "got: %s, expected: %s", pkType, "/cosmos.crypto.ed25519.PubKey", + ) + } + + pubKeyBytes, err := base64.StdEncoding.DecodeString(keyStr) + if err != nil { + return tmprotocrypto.PublicKey{}, err + } + + consumerTMPublicKey := tmprotocrypto.PublicKey{ + Sum: &tmprotocrypto.PublicKey_Ed25519{ + Ed25519: pubKeyBytes, + }, + } + + return consumerTMPublicKey, nil +} + // GetValidatorConsumerPubKey returns a validator's public key assigned for a consumer chain func (k Keeper) GetValidatorConsumerPubKey( ctx sdk.Context, @@ -323,12 +371,24 @@ func (k Keeper) AssignConsumerKey( } } - if _, found := k.GetValidatorByConsumerAddr(ctx, chainID, consumerAddr); found { + if existingProviderAddr, found := k.GetValidatorByConsumerAddr(ctx, chainID, consumerAddr); found { // consumer key is already in use - // prevent multiple validators from assigning the same key - return errorsmod.Wrapf( - types.ErrConsumerKeyInUse, "a validator has assigned the consumer key already", - ) + if providerAddr.Address.Equals(existingProviderAddr.Address) { + // the validator itself is the one already using the consumer key, + // just do a noop + k.Logger(ctx).Info("tried to assign a consumer key that is already assigned to the validator", + "consumer chainID", chainID, + "validator", providerAddr.String(), + "consumer consensus addr", consumerAddr.String(), + ) + return nil + } else { + // the validators are different -> throw an error to + // prevent multiple validators from assigning the same key + return errorsmod.Wrapf( + types.ErrConsumerKeyInUse, "a validator has assigned the consumer key already", + ) + } } // get the previous key assigned for this validator on this consumer chain diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index 46aefb1bbf..e721b39c33 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -17,11 +17,11 @@ import ( abci "github.com/cometbft/cometbft/abci/types" tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) const ChainID = "chainID" @@ -768,7 +768,10 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { }) } - nextValidators := k.ComputeNextEpochConsumerValSet(ctx, CHAINID, bondedValidators) + nextValidators := k.FilterValidators(ctx, CHAINID, bondedValidators, + func(providerAddr types.ProviderConsAddress) bool { + return true + }) updates = providerkeeper.DiffValidators(k.GetConsumerValSet(ctx, CHAINID), nextValidators) k.SetConsumerValSet(ctx, CHAINID, nextValidators) diff --git a/x/ccv/provider/keeper/msg_server.go b/x/ccv/provider/keeper/msg_server.go index 9a27d1d144..ee17fd8228 100644 --- a/x/ccv/provider/keeper/msg_server.go +++ b/x/ccv/provider/keeper/msg_server.go @@ -2,7 +2,6 @@ package keeper import ( "context" - "encoding/base64" errorsmod "cosmossdk.io/errors" @@ -10,11 +9,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) type msgServer struct { @@ -45,47 +43,11 @@ func (k msgServer) AssignConsumerKey(goCtx context.Context, msg *types.MsgAssign return nil, stakingtypes.ErrNoValidatorFound } - // parse consumer key as long as it's in the right format - pkType, keyStr, err := types.ParseConsumerKeyFromJson(msg.ConsumerKey) + consumerTMPublicKey, err := k.ParseConsumerKey(msg.ConsumerKey) if err != nil { return nil, err } - // Note: the correct way to decide if a key type is supported is to check the - // consensus params. However this functionality was disabled in https://github.com/cosmos/interchain-security/pull/916 - // as a quick way to get ed25519 working, avoiding amino/proto-any marshalling issues. - - // make sure the consumer key type is supported - // cp := ctx.ConsensusParams() - // if cp != nil && cp.Validator != nil { - // if !tmstrings.StringInSlice(pkType, cp.Validator.PubKeyTypes) { - // return nil, errorsmod.Wrapf( - // stakingtypes.ErrValidatorPubKeyTypeNotSupported, - // "got: %s, expected one of: %s", pkType, cp.Validator.PubKeyTypes, - // ) - // } - // } - - // For now, only accept ed25519. - // TODO: decide what types should be supported. - if pkType != "/cosmos.crypto.ed25519.PubKey" { - return nil, errorsmod.Wrapf( - stakingtypes.ErrValidatorPubKeyTypeNotSupported, - "got: %s, expected: %s", pkType, "/cosmos.crypto.ed25519.PubKey", - ) - } - - pubKeyBytes, err := base64.StdEncoding.DecodeString(keyStr) - if err != nil { - return nil, err - } - - consumerTMPublicKey := tmprotocrypto.PublicKey{ - Sum: &tmprotocrypto.PublicKey_Ed25519{ - Ed25519: pubKeyBytes, - }, - } - if err := k.Keeper.AssignConsumerKey(ctx, msg.ChainId, validator, consumerTMPublicKey); err != nil { return nil, err } @@ -174,3 +136,114 @@ func (k msgServer) SubmitConsumerDoubleVoting(goCtx context.Context, msg *types. return &types.MsgSubmitConsumerDoubleVotingResponse{}, nil } + +func (k msgServer) OptIn(goCtx context.Context, msg *types.MsgOptIn) (*types.MsgOptInResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + valAddress, err := sdk.ValAddressFromBech32(msg.ProviderAddr) + if err != nil { + return nil, err + } + + // validator must already be registered + validator, found := k.stakingKeeper.GetValidator(ctx, valAddress) + if !found { + return nil, stakingtypes.ErrNoValidatorFound + } + + consAddrTmp, err := validator.GetConsAddr() + if err != nil { + return nil, err + } + providerConsAddr := types.NewProviderConsAddress(consAddrTmp) + + if msg.ConsumerKey != "" { + err = k.Keeper.HandleOptIn(ctx, msg.ChainId, providerConsAddr, &msg.ConsumerKey) + } else { + err = k.Keeper.HandleOptIn(ctx, msg.ChainId, providerConsAddr, nil) + } + + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeOptIn, + sdk.NewAttribute(types.AttributeProviderValidatorAddress, msg.ProviderAddr), + sdk.NewAttribute(types.AttributeConsumerConsensusPubKey, msg.ConsumerKey), + ), + }) + + return &types.MsgOptInResponse{}, nil +} + +func (k msgServer) OptOut(goCtx context.Context, msg *types.MsgOptOut) (*types.MsgOptOutResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + valAddress, err := sdk.ValAddressFromBech32(msg.ProviderAddr) + if err != nil { + return nil, err + } + + // validator must already be registered + validator, found := k.stakingKeeper.GetValidator(ctx, valAddress) + if !found { + return nil, stakingtypes.ErrNoValidatorFound + } + + consAddrTmp, err := validator.GetConsAddr() + if err != nil { + return nil, err + } + providerConsAddr := types.NewProviderConsAddress(consAddrTmp) + + err = k.Keeper.HandleOptOut(ctx, msg.ChainId, providerConsAddr) + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeOptOut, + sdk.NewAttribute(types.AttributeProviderValidatorAddress, msg.ProviderAddr), + ), + }) + + return &types.MsgOptOutResponse{}, nil +} + +func (k msgServer) SetConsumerCommissionRate(goCtx context.Context, msg *types.MsgSetConsumerCommissionRate) (*types.MsgSetConsumerCommissionRateResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + providerValidatorAddr, err := sdk.ValAddressFromBech32(msg.ProviderAddr) + if err != nil { + return nil, err + } + + // validator must already be registered + validator, found := k.stakingKeeper.GetValidator(ctx, providerValidatorAddr) + if !found { + return nil, stakingtypes.ErrNoValidatorFound + } + + consAddr, err := validator.GetConsAddr() + if err != nil { + return nil, err + } + + if err := k.HandleSetConsumerCommissionRate(ctx, msg.ChainId, types.NewProviderConsAddress(consAddr), msg.Rate); err != nil { + return nil, err + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeSetConsumerCommissionRate, + sdk.NewAttribute(types.AttributeConsumerChainID, msg.ChainId), + sdk.NewAttribute(types.AttributeProviderValidatorAddress, msg.ProviderAddr), + sdk.NewAttribute(types.AttributeConsumerCommissionRate, msg.Rate.String()), + ), + }) + + return &types.MsgSetConsumerCommissionRateResponse{}, nil +} diff --git a/x/ccv/provider/keeper/params.go b/x/ccv/provider/keeper/params.go index f74baf656e..3834358b0b 100644 --- a/x/ccv/provider/keeper/params.go +++ b/x/ccv/provider/keeper/params.go @@ -7,8 +7,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // GetTemplateClient returns the template client for provider proposals diff --git a/x/ccv/provider/keeper/params_test.go b/x/ccv/provider/keeper/params_test.go index 88175431c0..ccbd8629d6 100644 --- a/x/ccv/provider/keeper/params_test.go +++ b/x/ccv/provider/keeper/params_test.go @@ -11,8 +11,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) // TestParams tests the getting/setting of provider ccv module params. diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go new file mode 100644 index 0000000000..9020a3db82 --- /dev/null +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -0,0 +1,295 @@ +package keeper + +import ( + "fmt" + "sort" + + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" +) + +// HandleOptIn prepares validator `providerAddr` to opt in to `chainID` with an optional `consumerKey` consumer public key. +// Note that the validator only opts in at the end of an epoch. +func (k Keeper) HandleOptIn(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress, consumerKey *string) error { + if !k.IsConsumerProposedOrRegistered(ctx, chainID) { + return errorsmod.Wrapf( + types.ErrUnknownConsumerChainId, + "opting in to an unknown consumer chain, with id: %s", chainID) + } + + k.SetOptedIn(ctx, chainID, providerAddr) + + if consumerKey != nil { + consumerTMPublicKey, err := k.ParseConsumerKey(*consumerKey) + if err != nil { + return err + } + + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, providerAddr.Address) + if !found { + return stakingtypes.ErrNoValidatorFound + } + + err = k.AssignConsumerKey(ctx, chainID, validator, consumerTMPublicKey) + if err != nil { + return err + } + } + + return nil +} + +// HandleOptOut prepares validator `providerAddr` to opt out from running `chainID`. +// Note that the validator only opts out at the end of an epoch. +func (k Keeper) HandleOptOut(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress) error { + if _, found := k.GetConsumerClientId(ctx, chainID); !found { + // A validator can only opt out from a running chain. We check this by checking the consumer client id, because + // `SetConsumerClientId` is set when the chain starts in `CreateConsumerClientInCachedCtx` of `BeginBlockInit`. + return errorsmod.Wrapf( + types.ErrUnknownConsumerChainId, + "opting out of an unknown or not running consumer chain, with id: %s", chainID) + } + + if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { + // a validator cannot opt out from a Top N chain if the validator is in the Top N validators + validator, validatorFound := k.stakingKeeper.GetValidatorByConsAddr(ctx, providerAddr.ToSdkConsAddr()) + if !validatorFound { + return errorsmod.Wrapf( + stakingtypes.ErrNoValidatorFound, + "validator with consensus address %s could not be found", providerAddr.ToSdkConsAddr()) + } + power := k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()) + minPowerToOptIn, err := k.ComputeMinPowerToOptIn(ctx, chainID, k.stakingKeeper.GetLastValidators(ctx), topN) + + if err != nil || power >= minPowerToOptIn { + return errorsmod.Wrapf( + types.ErrCannotOptOutFromTopN, + "validator with power (%d) cannot opt out from Top N chain because all validators"+ + "with at least %d power have to validate", power, minPowerToOptIn) + } + } + + k.DeleteOptedIn(ctx, chainID, providerAddr) + return nil +} + +// OptInTopNValidators opts in to `chainID` all the `bondedValidators` that have at least `minPowerToOptIn` power +func (k Keeper) OptInTopNValidators(ctx sdk.Context, chainID string, bondedValidators []stakingtypes.Validator, minPowerToOptIn int64) { + for _, val := range bondedValidators { + power := k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()) + if power >= minPowerToOptIn { + consAddr, err := val.GetConsAddr() + if err != nil { + k.Logger(ctx).Error("could not retrieve validators consensus address", + "validator", val, + "error", err) + continue + } + + // if validator already exists it gets overwritten + k.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(consAddr)) + } // else validators that do not belong to the top N validators but were opted in, remain opted in + } +} + +// ComputeMinPowerToOptIn returns the minimum power needed for a validator (from the bonded validators) +// to belong to the `topN` validators. `chainID` is only used for logging purposes. +func (k Keeper) ComputeMinPowerToOptIn(ctx sdk.Context, chainID string, bondedValidators []stakingtypes.Validator, topN uint32) (int64, error) { + if topN == 0 || topN > 100 { + return 0, fmt.Errorf("trying to compute minimum power with an incorrect topN value (%d)."+ + "topN has to be between (0, 100]", topN) + } + + totalPower := sdk.ZeroDec() + var powers []int64 + + for _, val := range bondedValidators { + power := k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()) + powers = append(powers, power) + totalPower = totalPower.Add(sdk.NewDecFromInt(sdk.NewInt(power))) + } + + // sort by powers descending + sort.Slice(powers, func(i, j int) bool { + return powers[i] > powers[j] + }) + + topNThreshold := sdk.NewDecFromInt(sdk.NewInt(int64(topN))).QuoInt64(int64(100)) + powerSum := sdk.ZeroDec() + for _, power := range powers { + powerSum = powerSum.Add(sdk.NewDecFromInt(sdk.NewInt(power))) + if powerSum.Quo(totalPower).GTE(topNThreshold) { + return power, nil + } + } + + // We should never reach this point because the topN can be up to 1.0 (100%) and in the above `for` loop we + // perform an equal comparison as well (`GTE`). + return 0, fmt.Errorf("should never reach this point with topN (%d), totalPower (%d), and powerSum (%d)", topN, totalPower, powerSum) +} + +// CapValidatorSet caps the provided `validators` if chain `chainID` is an Opt In chain with a validator-set cap. If cap +// is `k`, `CapValidatorSet` returns the first `k` validators from `validators` with the highest power. +func (k Keeper) CapValidatorSet(ctx sdk.Context, chainID string, validators []types.ConsumerValidator) []types.ConsumerValidator { + if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { + // is a no-op if the chain is a Top N chain + return validators + } + + if validatorSetCap, found := k.GetValidatorSetCap(ctx, chainID); found && validatorSetCap != 0 { + sort.Slice(validators, func(i, j int) bool { + return validators[i].Power > validators[j].Power + }) + + minNumberOfValidators := 0 + if len(validators) < int(validatorSetCap) { + minNumberOfValidators = len(validators) + } else { + minNumberOfValidators = int(validatorSetCap) + } + return validators[:minNumberOfValidators] + } else { + return validators + } +} + +// CapValidatorsPower caps the power of the validators on chain `chainID` and returns an updated slice of validators +// with their new powers. Works on a best-basis effort because there are cases where we cannot guarantee that all validators +// on the consumer chain have less power than the set validators-power cap. For example, if we have 10 validators and +// the power cap is set to 5%, we need at least one validator to have more than 10% of the voting power on the consumer chain. +func (k Keeper) CapValidatorsPower(ctx sdk.Context, chainID string, validators []types.ConsumerValidator) []types.ConsumerValidator { + if p, found := k.GetValidatorsPowerCap(ctx, chainID); found && p > 0 { + return NoMoreThanPercentOfTheSum(validators, p) + } else { + // is a no-op if power cap is not set for `chainID` + return validators + } +} + +// sum is a helper function to sum all the validators' power +func sum(validators []types.ConsumerValidator) int64 { + s := int64(0) + for _, v := range validators { + s = s + v.Power + } + return s +} + +// NoMoreThanPercentOfTheSum returns a set of validators with updated powers such that no validator has more than the +// provided `percent` of the sum of all validators' powers. Operates on a best-effort basis. +func NoMoreThanPercentOfTheSum(validators []types.ConsumerValidator, percent uint32) []types.ConsumerValidator { + // Algorithm's idea + // ---------------- + // Consider the validators' powers to be `a_1, a_2, ... a_n` and `p` to be the percent in [1, 100]. Now, consider + // the sum `s = a_1 + a_2 + ... + a_n`. Then `maxPower = s * p / 100 <=> 100 * maxPower = s * p`. + // The problem of capping the validators' powers to be no more than `p` has no solution if `(100 / n) > p`. For example, + // for n = 10 and p = 5 we do not have a solution. We would need at least one validator with power greater than 10% + // for a solution to exist. + // So, if `(100 / n) > p` there's no solution. We know that `100 * maxPower = s * p` and so `(100 / n) > (100 * maxPower) / s` + // `100 * s > 100 * maxPower * n <=> s > maxPower * n`. Thus, we do not have a solution if `s > n * maxPower`. + + // If `s <= n * maxPower` the idea of the algorithm is rather simple. + // - Compute the `maxPower` a validator must have so that it does not have more than `percent` of the voting power. + // - If a validator `v` has power `p`, then: + // - if `p > maxPower` we set `v`'s power to `maxPower` and distribute the `p - maxPower` to validators that + // have less than `maxPower` power. This way, the total sum remains the same and no validator has more than + // `maxPower` and so the power capping is satisfied. + // - Note that in order to avoid setting multiple validators to `maxPower`, we first compute all the `remainingPower` + // we have to distribute and then attempt to add `remainingPower / validatorsWithPowerLessThanMaxPower` to each validator. + // To guarantee that the sum remains the same after the distribution of powers, we sort the powers in descending + // order. This way, we first attempt to add `remainingPower / validatorsWithPowerLessThanMaxPower` to validators + // with greater power and if we cannot add the `remainingPower / validatorsWithPowerLessThanMaxPower` without + // exceeding `maxPower`, we just add enough to reach `maxPower` and add the remaining power to validators with smaller + // power. + + // If `s > n * maxPower` there's no solution and the algorithm would set everything to `maxPower`. + // ---------------- + + // Computes `(sum(validators) * percent) / 100`. Because `sdk.Dec` does not provide a `Floor` function, but only + // a `Ceil` one, we use `Ceil` and subtract one. + maxPower := sdk.NewDec(sum(validators)).Mul(sdk.NewDec(int64(percent))).QuoInt64(100).Ceil().RoundInt64() - 1 + + if maxPower == 0 { + // edge case: set `maxPower` to 1 to avoid setting the power of a validator to 0 + maxPower = 1 + } + + // Sort by `.Power` in decreasing order. Sorting in descending order is needed because otherwise we would have cases + // like this `powers =[60, 138, 559]` and `p = 35%` where sum is `757` and `maxValue = 264`. + // Because `559 - 264 = 295` we have to distribute 295 to the first 2 numbers (`295/2 = 147` to each number). However, + // note that `138 + 147 > 264`. If we were to add 147 to 60 first, then we cannot give the remaining 147 to 138. + // So, the idea is to first give `126 (= 264 - 138)` to 138, so it becomes 264, and then add `21 (=147 - 26) + 147` to 60. + sort.Slice(validators, func(i, j int) bool { + return validators[i].Power > validators[j].Power + }) + + // `remainingPower` is to be distributed to validators that have power less than `maxPower` + remainingPower := int64(0) + validatorsWithPowerLessThanMaxPower := 0 + for _, v := range validators { + if v.Power >= maxPower { + remainingPower = remainingPower + (v.Power - maxPower) + } else { + validatorsWithPowerLessThanMaxPower++ + } + } + + updatedValidators := make([]types.ConsumerValidator, len(validators)) + + powerPerValidator := int64(0) + remainingValidators := int64(validatorsWithPowerLessThanMaxPower) + if remainingValidators != 0 { + // power to give to each validator in order to distribute the `remainingPower` + powerPerValidator = remainingPower / remainingValidators + } + + for i, v := range validators { + if v.Power >= maxPower { + updatedValidators[i] = validators[i] + updatedValidators[i].Power = maxPower + } else if v.Power+powerPerValidator >= maxPower { + updatedValidators[i] = validators[i] + updatedValidators[i].Power = maxPower + remainingPower = remainingPower - (maxPower - v.Power) + remainingValidators-- + } else { + updatedValidators[i] = validators[i] + updatedValidators[i].Power = v.Power + powerPerValidator + remainingPower = remainingPower - (updatedValidators[i].Power - validators[i].Power) + remainingValidators-- + } + if remainingValidators == 0 { + continue + } + powerPerValidator = remainingPower / remainingValidators + } + + return updatedValidators +} + +// FilterOptedInAndAllowAndDenylistedPredicate filters the opted-in validators that are allowlisted and not denylisted +func (k Keeper) FilterOptedInAndAllowAndDenylistedPredicate(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress) bool { + // only consider opted-in validators + return k.IsOptedIn(ctx, chainID, providerAddr) && + // if an allowlist is declared, only consider allowlisted validators + (k.IsAllowlistEmpty(ctx, chainID) || + k.IsAllowlisted(ctx, chainID, providerAddr)) && + // if a denylist is declared, only consider denylisted validators + (k.IsDenylistEmpty(ctx, chainID) || + !k.IsDenylisted(ctx, chainID, providerAddr)) +} + +// ComputeNextValidators computes the validators for the upcoming epoch based on the currently `bondedValidators`. +func (k Keeper) ComputeNextValidators(ctx sdk.Context, chainID string, bondedValidators []stakingtypes.Validator) []types.ConsumerValidator { + nextValidators := k.FilterValidators(ctx, chainID, bondedValidators, + func(providerAddr types.ProviderConsAddress) bool { + return k.FilterOptedInAndAllowAndDenylistedPredicate(ctx, chainID, providerAddr) + }) + + nextValidators = k.CapValidatorSet(ctx, chainID, nextValidators) + return k.CapValidatorsPower(ctx, chainID, nextValidators) +} diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go new file mode 100644 index 0000000000..42ff7a14ab --- /dev/null +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -0,0 +1,676 @@ +package keeper_test + +import ( + "bytes" + "math" + "sort" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + "pgregory.net/rapid" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/cometbft/cometbft/proto/tendermint/crypto" + + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" +) + +func TestHandleOptIn(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + providerAddr := types.NewProviderConsAddress([]byte("providerAddr")) + + // trying to opt in to a non-proposed and non-registered chain returns an error + require.Error(t, providerKeeper.HandleOptIn(ctx, "unknownChainID", providerAddr, nil)) + + providerKeeper.SetProposedConsumerChain(ctx, "chainID", 1) + require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", providerAddr)) + providerKeeper.HandleOptIn(ctx, "chainID", providerAddr, nil) + require.True(t, providerKeeper.IsOptedIn(ctx, "chainID", providerAddr)) +} + +func TestHandleOptInWithConsumerKey(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // generate a consensus public key for the provider + providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{1}).PubKey() + consAddr := sdk.ConsAddress(providerConsPubKey.Address()) + providerAddr := types.NewProviderConsAddress(consAddr) + + calls := []*gomock.Call{ + mocks.MockStakingKeeper.EXPECT(). + GetValidatorByConsAddr(gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx sdk.Context, addr sdk.ConsAddress) (stakingtypes.Validator, bool) { + if addr.Equals(providerAddr.Address) { + // Given `providerAddr`, `GetValidatorByConsAddr` returns a validator with the + // exact same `ConsensusPubkey` + pkAny, _ := codectypes.NewAnyWithValue(providerConsPubKey) + return stakingtypes.Validator{ConsensusPubkey: pkAny}, true + } else { + // for any other consensus address, we cannot find a validator + return stakingtypes.Validator{}, false + } + }).Times(2), + } + + gomock.InOrder(calls...) + providerKeeper.SetProposedConsumerChain(ctx, "chainID", 1) + + // create a sample consumer key to assign to the `providerAddr` validator + // on the consumer chain with id `chainID` + consumerKey := "{\"@type\":\"/cosmos.crypto.ed25519.PubKey\",\"key\":\"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=\"}" + expectedConsumerPubKey, _ := providerKeeper.ParseConsumerKey(consumerKey) + + err := providerKeeper.HandleOptIn(ctx, "chainID", providerAddr, &consumerKey) + require.NoError(t, err) + + // assert that the consumeKey was assigned to `providerAddr` validator on chain with id `chainID` + actualConsumerPubKey, found := providerKeeper.GetValidatorConsumerPubKey(ctx, "chainID", providerAddr) + require.True(t, found) + require.Equal(t, expectedConsumerPubKey, actualConsumerPubKey) + + // assert that the `consumerAddr` to `providerAddr` association was set as well + consumerAddr, _ := ccvtypes.TMCryptoPublicKeyToConsAddr(actualConsumerPubKey) + actualProviderConsAddr, found := providerKeeper.GetValidatorByConsumerAddr(ctx, "chainID", types.NewConsumerConsAddress(consumerAddr)) + require.Equal(t, providerAddr, actualProviderConsAddr) +} + +func TestHandleOptOut(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + providerAddr := types.NewProviderConsAddress([]byte("providerAddr")) + + // trying to opt out from a not running chain returns an error + require.Error(t, providerKeeper.HandleOptOut(ctx, "unknownChainID", providerAddr)) + + // set a consumer client so that the chain is considered running + providerKeeper.SetConsumerClientId(ctx, "chainID", "clientID") + + // if validator (`providerAddr`) is already opted in, then an opt-out would remove this validator + providerKeeper.SetOptedIn(ctx, "chainID", providerAddr) + require.True(t, providerKeeper.IsOptedIn(ctx, "chainID", providerAddr)) + providerKeeper.HandleOptOut(ctx, "chainID", providerAddr) + require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", providerAddr)) +} + +func TestHandleOptOutFromTopNChain(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainID := "chainID" + + // set a consumer client so that the chain is considered running + providerKeeper.SetConsumerClientId(ctx, chainID, "clientID") + + // set the chain as Top 50 and create 4 validators with 10%, 20%, 30%, and 40% of the total voting power + // respectively + providerKeeper.SetTopN(ctx, "chainID", 50) + valA := createStakingValidator(ctx, mocks, 1, 1) // 10% of the total voting power (can opt out) + valAConsAddr, _ := valA.GetConsAddr() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valAConsAddr).Return(valA, true).AnyTimes() + valB := createStakingValidator(ctx, mocks, 2, 2) // 20% of the total voting power (can opt out) + valBConsAddr, _ := valB.GetConsAddr() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valBConsAddr).Return(valB, true).AnyTimes() + valC := createStakingValidator(ctx, mocks, 3, 3) // 30% of the total voting power (cannot opt out) + valCConsAddr, _ := valC.GetConsAddr() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valCConsAddr).Return(valC, true).AnyTimes() + valD := createStakingValidator(ctx, mocks, 4, 4) // 40% of the total voting power (cannot opt out) + valDConsAddr, _ := valD.GetConsAddr() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valDConsAddr).Return(valD, true).AnyTimes() + + mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Return([]stakingtypes.Validator{valA, valB, valC, valD}).AnyTimes() + + // opt in all validators + providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(valAConsAddr)) + providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(valBConsAddr)) + providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(valCConsAddr)) + providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(valDConsAddr)) + + // validators A and B can opt out because they belong the bottom 30% of validators + require.NoError(t, providerKeeper.HandleOptOut(ctx, chainID, types.NewProviderConsAddress(valAConsAddr))) + require.NoError(t, providerKeeper.HandleOptOut(ctx, chainID, types.NewProviderConsAddress(valBConsAddr))) + + // validators C and D cannot opt out because C has 30% of the voting power and D has 40% of the voting power + // and hence both are needed to keep validating a Top 50 chain + require.Error(t, providerKeeper.HandleOptOut(ctx, chainID, types.NewProviderConsAddress(valCConsAddr))) + require.Error(t, providerKeeper.HandleOptOut(ctx, chainID, types.NewProviderConsAddress(valDConsAddr))) + + // opting out a validator that cannot be found from a Top N chain should also return an error + notFoundValidator := createStakingValidator(ctx, mocks, 5, 5) + notFoundValidatorConsAddr, _ := notFoundValidator.GetConsAddr() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, notFoundValidatorConsAddr). + Return(stakingtypes.Validator{}, false) + require.Error(t, providerKeeper.HandleOptOut(ctx, chainID, types.NewProviderConsAddress(notFoundValidatorConsAddr))) +} + +func TestHandleSetConsumerCommissionRate(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + providerAddr := types.NewProviderConsAddress([]byte("providerAddr")) + + // trying to set a commission rate to a unknown consumer chain + require.Error(t, providerKeeper.HandleSetConsumerCommissionRate(ctx, "unknownChainID", providerAddr, sdk.ZeroDec())) + + // setup a pending consumer chain + chainID := "pendingChainID" + providerKeeper.SetPendingConsumerAdditionProp(ctx, &types.ConsumerAdditionProposal{ChainId: chainID}) + + // check that there's no commission rate set for the validator yet + _, found := providerKeeper.GetConsumerCommissionRate(ctx, chainID, providerAddr) + require.False(t, found) + + mocks.MockStakingKeeper.EXPECT().MinCommissionRate(ctx).Return(sdk.ZeroDec()).Times(1) + require.NoError(t, providerKeeper.HandleSetConsumerCommissionRate(ctx, chainID, providerAddr, sdk.OneDec())) + + // check that the commission rate is now set + cr, found := providerKeeper.GetConsumerCommissionRate(ctx, chainID, providerAddr) + require.Equal(t, sdk.OneDec(), cr) + require.True(t, found) + + // set minimum rate of 1/2 + commissionRate := sdk.NewDec(1).Quo(sdk.NewDec(2)) + mocks.MockStakingKeeper.EXPECT().MinCommissionRate(ctx).Return(commissionRate).AnyTimes() + + // try to set a rate slightly below the minimum + require.Error(t, providerKeeper.HandleSetConsumerCommissionRate( + ctx, + chainID, + providerAddr, + commissionRate.Sub(sdk.NewDec(1).Quo(sdk.NewDec(100)))), // 0.5 - 0.01 + "commission rate should be rejected (below min), but is not", + ) + + // set a valid commission equal to the minimum + require.NoError(t, providerKeeper.HandleSetConsumerCommissionRate(ctx, chainID, providerAddr, commissionRate)) + // check that the rate was set + cr, found = providerKeeper.GetConsumerCommissionRate(ctx, chainID, providerAddr) + require.Equal(t, commissionRate, cr) + require.True(t, found) +} + +func TestOptInTopNValidators(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // create 4 validators with powers 1, 2, 3, and 1 respectively + valA := createStakingValidator(ctx, mocks, 1, 1) + valAConsAddr, _ := valA.GetConsAddr() + valB := createStakingValidator(ctx, mocks, 2, 2) + valBConsAddr, _ := valB.GetConsAddr() + valC := createStakingValidator(ctx, mocks, 3, 3) + valCConsAddr, _ := valC.GetConsAddr() + valD := createStakingValidator(ctx, mocks, 4, 1) + valDConsAddr, _ := valD.GetConsAddr() + + // Start Test 1: opt in all validators with power >= 0 + providerKeeper.OptInTopNValidators(ctx, "chainID", []stakingtypes.Validator{valA, valB, valC, valD}, 0) + expectedOptedInValidators := []types.ProviderConsAddress{ + types.NewProviderConsAddress(valAConsAddr), + types.NewProviderConsAddress(valBConsAddr), + types.NewProviderConsAddress(valCConsAddr), + types.NewProviderConsAddress(valDConsAddr), + } + actualOptedInValidators := providerKeeper.GetAllOptedIn(ctx, "chainID") + + // sort validators first to be able to compare + sortUpdates := func(addresses []types.ProviderConsAddress) { + sort.Slice(addresses, func(i, j int) bool { + return bytes.Compare(addresses[i].ToSdkConsAddr(), addresses[j].ToSdkConsAddr()) < 0 + }) + } + + sortUpdates(expectedOptedInValidators) + sortUpdates(actualOptedInValidators) + require.Equal(t, expectedOptedInValidators, actualOptedInValidators) + + // reset state for the upcoming checks + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valAConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valBConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valCConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valDConsAddr)) + + // Start Test 2: opt in all validators with power >= 1 + // We expect the same `expectedOptedInValidators` as when we opted in all validators with power >= 0 because the + // validators with the smallest power have power == 1 + providerKeeper.OptInTopNValidators(ctx, "chainID", []stakingtypes.Validator{valA, valB, valC, valD}, 0) + actualOptedInValidators = providerKeeper.GetAllOptedIn(ctx, "chainID") + sortUpdates(actualOptedInValidators) + require.Equal(t, expectedOptedInValidators, actualOptedInValidators) + + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valAConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valBConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valCConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valDConsAddr)) + + // Start Test 3: opt in all validators with power >= 2 and hence we do not expect to opt in validator A + providerKeeper.OptInTopNValidators(ctx, "chainID", []stakingtypes.Validator{valA, valB, valC, valD}, 2) + expectedOptedInValidators = []types.ProviderConsAddress{ + types.NewProviderConsAddress(valBConsAddr), + types.NewProviderConsAddress(valCConsAddr), + } + actualOptedInValidators = providerKeeper.GetAllOptedIn(ctx, "chainID") + + // sort validators first to be able to compare + sortUpdates(expectedOptedInValidators) + sortUpdates(actualOptedInValidators) + require.Equal(t, expectedOptedInValidators, actualOptedInValidators) + + // reset state for the upcoming checks + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valAConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valBConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valCConsAddr)) + providerKeeper.DeleteOptedIn(ctx, "chainID", types.NewProviderConsAddress(valDConsAddr)) + + // Start Test 4: opt in all validators with power >= 4 and hence we do not expect any opted-in validators + providerKeeper.OptInTopNValidators(ctx, "chainID", []stakingtypes.Validator{valA, valB, valC, valD}, 4) + require.Empty(t, providerKeeper.GetAllOptedIn(ctx, "chainID")) +} + +func TestComputeMinPowerToOptIn(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // create 5 validators with powers 1, 3, 5, 6, 10 (not in that order) with total power of 25 (= 1 + 3 + 5 + 6 + 10) + // such that: + // validator power => cumulative share + // 10 => 40% + // 6 => 64% + // 5 => 84% + // 3 => 96% + // 1 => 100% + + bondedValidators := []stakingtypes.Validator{ + createStakingValidator(ctx, mocks, 1, 5), + createStakingValidator(ctx, mocks, 2, 10), + createStakingValidator(ctx, mocks, 3, 3), + createStakingValidator(ctx, mocks, 4, 1), + createStakingValidator(ctx, mocks, 5, 6), + } + + m, err := providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 100) + require.NoError(t, err) + require.Equal(t, int64(1), m) + + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 97) + require.NoError(t, err) + require.Equal(t, int64(1), m) + + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 96) + require.NoError(t, err) + require.Equal(t, int64(3), m) + + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 85) + require.NoError(t, err) + require.Equal(t, int64(3), m) + + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 84) + require.NoError(t, err) + require.Equal(t, int64(5), m) + + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 65) + require.NoError(t, err) + require.Equal(t, int64(5), m) + + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 64) + require.NoError(t, err) + require.Equal(t, int64(6), m) + + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 41) + require.NoError(t, err) + require.Equal(t, int64(6), m) + + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 40) + require.NoError(t, err) + require.Equal(t, int64(10), m) + + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 1) + require.NoError(t, err) + require.Equal(t, int64(10), m) + + // exceptional case when we erroneously call with `topN == 0` or `topN > 100` + _, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 0) + require.Error(t, err) + + _, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 101) + require.Error(t, err) +} + +// TestFilterOptedInAndAllowAndDenylistedPredicate returns true if `validator` is opted in, in `chainID. +func TestFilterOptedInAndAllowAndDenylistedPredicate(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + validator := createStakingValidator(ctx, mocks, 0, 1) + consAddr, _ := validator.GetConsAddr() + providerAddr := types.NewProviderConsAddress(consAddr) + + // with no allowlist or denylist, the validator has to be opted in, in order to consider it + require.False(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) + providerKeeper.SetOptedIn(ctx, "chainID", types.NewProviderConsAddress(consAddr)) + require.True(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) + + // create an allow list but do not add the validator `providerAddr` to it + validatorA := createStakingValidator(ctx, mocks, 1, 1) + consAddrA, _ := validatorA.GetConsAddr() + providerKeeper.SetAllowlist(ctx, "chainID", types.NewProviderConsAddress(consAddrA)) + require.False(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) + providerKeeper.SetAllowlist(ctx, "chainID", types.NewProviderConsAddress(consAddr)) + require.True(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) + + // create a denylist but do not add validator `providerAddr` to it + providerKeeper.SetDenylist(ctx, "chainID", types.NewProviderConsAddress(consAddrA)) + require.True(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) + // add validator `providerAddr` to the denylist + providerKeeper.SetDenylist(ctx, "chainID", types.NewProviderConsAddress(consAddr)) + require.False(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) +} + +func TestCapValidatorSet(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + validatorA := types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddrA"), + Power: 1, + ConsumerPublicKey: &crypto.PublicKey{}, + } + + validatorB := types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddrB"), + Power: 2, + ConsumerPublicKey: &crypto.PublicKey{}, + } + + validatorC := types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddrC"), + Power: 3, + ConsumerPublicKey: &crypto.PublicKey{}, + } + validators := []types.ConsumerValidator{validatorA, validatorB, validatorC} + + consumerValidators := providerKeeper.CapValidatorSet(ctx, "chainID", validators) + require.Equal(t, validators, consumerValidators) + + providerKeeper.SetValidatorSetCap(ctx, "chainID", 0) + consumerValidators = providerKeeper.CapValidatorSet(ctx, "chainID", validators) + require.Equal(t, validators, consumerValidators) + + providerKeeper.SetValidatorSetCap(ctx, "chainID", 100) + consumerValidators = providerKeeper.CapValidatorSet(ctx, "chainID", validators) + require.Equal(t, validators, consumerValidators) + + providerKeeper.SetValidatorSetCap(ctx, "chainID", 1) + consumerValidators = providerKeeper.CapValidatorSet(ctx, "chainID", validators) + require.Equal(t, []types.ConsumerValidator{validatorC}, consumerValidators) + + providerKeeper.SetValidatorSetCap(ctx, "chainID", 2) + consumerValidators = providerKeeper.CapValidatorSet(ctx, "chainID", validators) + require.Equal(t, []types.ConsumerValidator{validatorC, validatorB}, consumerValidators) + + providerKeeper.SetValidatorSetCap(ctx, "chainID", 3) + consumerValidators = providerKeeper.CapValidatorSet(ctx, "chainID", validators) + require.Equal(t, []types.ConsumerValidator{validatorC, validatorB, validatorA}, consumerValidators) +} + +func TestCapValidatorsPower(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + validatorA := types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddrA"), + Power: 1, + ConsumerPublicKey: &crypto.PublicKey{}, + } + + validatorB := types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddrB"), + Power: 2, + ConsumerPublicKey: &crypto.PublicKey{}, + } + + validatorC := types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddrC"), + Power: 3, + ConsumerPublicKey: &crypto.PublicKey{}, + } + + validatorD := types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddrD"), + Power: 4, + ConsumerPublicKey: &crypto.PublicKey{}, + } + + validators := []types.ConsumerValidator{validatorA, validatorB, validatorC, validatorD} + + expectedValidators := make([]types.ConsumerValidator, len(validators)) + copy(expectedValidators, validators) + expectedValidators[0].Power = 2 + expectedValidators[1].Power = 2 + expectedValidators[2].Power = 3 + expectedValidators[3].Power = 3 + + sortValidators := func(validators []types.ConsumerValidator) { + sort.Slice(validators, func(i, j int) bool { + return bytes.Compare(validators[i].ProviderConsAddr, validators[j].ProviderConsAddr) < 0 + }) + } + + // no capping takes place because validators power-cap is not set + cappedValidators := providerKeeper.CapValidatorsPower(ctx, "chainID", validators) + sortValidators(validators) + sortValidators(cappedValidators) + require.Equal(t, validators, cappedValidators) + + providerKeeper.SetValidatorsPowerCap(ctx, "chainID", 33) + cappedValidators = providerKeeper.CapValidatorsPower(ctx, "chainID", validators) + sortValidators(expectedValidators) + sortValidators(cappedValidators) + require.Equal(t, expectedValidators, cappedValidators) +} + +func TestNoMoreThanPercentOfTheSum(t *testing.T) { + // **impossible** case where we only have 9 powers, and we want that no number has more than 10% of the total sum + powers := []int64{1, 2, 3, 4, 5, 6, 7, 8, 9} + percent := uint32(10) + require.False(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) + + powers = []int64{1, 2, 3, 4, 5} + percent = 20 + require.True(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) + + powers = []int64{1, 2, 3, 4, 5} + percent = 21 + require.True(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) + + powers = []int64{1, 2, 3, 4, 5} + percent = 25 + require.True(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) + + powers = []int64{1, 2, 3, 4, 5} + percent = 32 + require.True(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) + + powers = []int64{1, 2, 3, 4, 5} + percent = 33 + require.True(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) + + powers = []int64{1, 2, 3, 4, 5} + percent = 34 + require.True(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) + + powers = []int64{1, 2, 3, 4, 5} + percent = 50 + require.True(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) +} + +func createConsumerValidators(powers []int64) []types.ConsumerValidator { + var validators []types.ConsumerValidator + for _, p := range powers { + validators = append(validators, types.ConsumerValidator{ + ProviderConsAddr: []byte("providerConsAddr"), + Power: p, + ConsumerPublicKey: &crypto.PublicKey{}, + }) + } + return validators +} + +// returns `true` if no validator in `validators` corresponds to more than `percent` of the total sum of all +// validators' powers +func noMoreThanPercent(validators []types.ConsumerValidator, percent uint32) bool { + sum := int64(0) + for _, v := range validators { + sum = sum + v.Power + } + + for _, v := range validators { + if (float64(v.Power)/float64(sum))*100.0 > float64(percent) { + return false + } + } + return true +} + +func sumPowers(vals []types.ConsumerValidator) int64 { + sum := int64(0) + for _, v := range vals { + sum += v.Power + } + return sum +} + +func CapSatisfiable(vals []types.ConsumerValidator, percent uint32) bool { + // 100 / len(vals) is what each validator gets if each has the same power. + // if this is more than the cap, it cannot be satisfied. + return float64(100)/float64(len(vals)) < float64(percent) +} + +func TestNoMoreThanPercentOfTheSumProps(t *testing.T) { + // define properties to test + + // capRespectedIfSatisfiable: if the cap can be respected, then it will be respected + capRespectedIfSatisfiable := func(valsBefore, valsAfter []types.ConsumerValidator, percent uint32) bool { + if CapSatisfiable(valsBefore, percent) { + return noMoreThanPercent(valsAfter, percent) + } + return true + } + + evenPowersIfCapCannotBeSatisfied := func(valsBefore, valsAfter []types.ConsumerValidator, percent uint32) bool { + if !CapSatisfiable(valsBefore, percent) { + // if the cap cannot be satisfied, each validator should have the same power + for _, valAfter := range valsAfter { + if valAfter.Power != valsAfter[0].Power { + return false + } + } + } + return true + } + + // fairness: if before, v1 has more power than v2, then afterwards v1 will not have less power than v2 + // (they might get the same power if they are both capped) + fairness := func(valsBefore, valsAfter []types.ConsumerValidator) bool { + for i, v := range valsBefore { + // find the validator after with the same address + vAfter := findConsumerValidator(t, v, valsAfter) + + // go through all other validators before (after this one, to avoid double checking) + for j := i + 1; j < len(valsBefore); j++ { + otherV := valsBefore[j] + otherVAfter := findConsumerValidator(t, otherV, valsAfter) + + // v has at least as much power before + if v.Power >= otherV.Power { + // then otherV should not have more power after + if vAfter.Power < otherVAfter.Power { + return false + } + } else { + // v has less power before + // then v should not have more power after + if vAfter.Power > otherVAfter.Power { + return false + } + } + } + } + return true + } + + // non-zero: v has non-zero power before IFF it has non-zero power after + nonZero := func(valsBefore, valsAfter []types.ConsumerValidator) bool { + for _, v := range valsBefore { + vAfter := findConsumerValidator(t, v, valsAfter) + if (v.Power == 0) != (vAfter.Power == 0) { + return false + } + } + return true + } + + // equalSumIfCapSatisfiable: the sum of the powers of the validators will not change if the cap can be satisfied + // (except for small changes by rounding errors) + equalSumIfCapSatisfiable := func(valsBefore, valsAfter []types.ConsumerValidator, percent uint32) bool { + if CapSatisfiable(valsBefore, percent) { + difference := math.Abs(float64(sumPowers(valsBefore) - sumPowers(valsAfter))) + if difference > 1 { + // if the difference is more than a rounding error, they are not equal + return false + } + } + return true + } + + // num validators: the number of validators will not change + equalNumVals := func(valsBefore, valsAfter []types.ConsumerValidator) bool { + return len(valsBefore) == len(valsAfter) + } + + // test setup for pbt + rapid.Check(t, func(t *rapid.T) { + powers := rapid.SliceOf(rapid.Int64Range(1, 1000000000000)).Draw(t, "powers") + percent := uint32(rapid.Int32Range(1, 100).Draw(t, "percent")) + + consumerValidators := createConsumerValidators(powers) + cappedValidators := keeper.NoMoreThanPercentOfTheSum(consumerValidators, percent) + + t.Log("can the cap be satisfied: ", CapSatisfiable(consumerValidators, percent)) + t.Log("before: ", consumerValidators) + t.Log("after: ", cappedValidators) + + // check properties + require.True(t, capRespectedIfSatisfiable(consumerValidators, cappedValidators, percent)) + require.True(t, evenPowersIfCapCannotBeSatisfied(consumerValidators, cappedValidators, percent)) + require.True(t, fairness(consumerValidators, cappedValidators)) + require.True(t, nonZero(consumerValidators, cappedValidators)) + require.True(t, equalSumIfCapSatisfiable(consumerValidators, cappedValidators, percent), "sum before: %v, sum after: %v", sumPowers(consumerValidators), sumPowers(cappedValidators)) + require.True(t, equalNumVals(consumerValidators, cappedValidators), "num before: %v, num after: %v", len(consumerValidators), len(cappedValidators)) + }) +} + +func findConsumerValidator(t *testing.T, v types.ConsumerValidator, valsAfter []types.ConsumerValidator) *types.ConsumerValidator { + var vAfter *types.ConsumerValidator + for _, vA := range valsAfter { + if bytes.Equal(v.ProviderConsAddr, vA.ProviderConsAddr) { + vAfter = &vA + break + } + } + if vAfter == nil { + t.Fatalf("could not find validator with address %v in validators after \n validators after capping: %v", v.ProviderConsAddr, valsAfter) + } + return vAfter +} diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index c7a122b497..67d3c91bca 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -18,8 +18,8 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // HandleConsumerAdditionProposal will receive the consumer chain's client state from the proposal. @@ -189,6 +189,12 @@ func (k Keeper) StopConsumerChain(ctx sdk.Context, chainID string, closeChan boo k.DeleteVscSendTimestampsForConsumer(ctx, chainID) } + // delete consumer commission rate + provAddrs := k.GetAllCommissionRateValidators(ctx, chainID) + for _, addr := range provAddrs { + k.DeleteConsumerCommissionRate(ctx, chainID, addr) + } + k.DeleteInitChainHeight(ctx, chainID) k.DeleteSlashAcks(ctx, chainID) k.DeletePendingVSCPackets(ctx, chainID) @@ -213,6 +219,15 @@ func (k Keeper) StopConsumerChain(ctx sdk.Context, chainID string, closeChan boo k.DeleteUnbondingOpIndex(ctx, chainID, unbondingOpsIndex.VscId) } + k.DeleteTopN(ctx, chainID) + k.DeleteValidatorsPowerCap(ctx, chainID) + k.DeleteValidatorSetCap(ctx, chainID) + k.DeleteAllowlist(ctx, chainID) + k.DeleteDenylist(ctx, chainID) + + k.DeleteAllOptedIn(ctx, chainID) + k.DeleteConsumerValSet(ctx, chainID) + k.Logger(ctx).Info("consumer chain removed from provider", "chainID", chainID) return nil @@ -269,7 +284,16 @@ func (k Keeper) MakeConsumerGenesis( bondedValidators = append(bondedValidators, val) } - nextValidators := k.ComputeNextEpochConsumerValSet(ctx, chainID, bondedValidators) + if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { + // in a Top-N chain, we automatically opt in all validators that belong to the top N + minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, bondedValidators, prop.Top_N) + if err == nil { + k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) + } + } + + nextValidators := k.ComputeNextValidators(ctx, chainID, bondedValidators) + k.SetConsumerValSet(ctx, chainID, nextValidators) // get the initial updates with the latest set consumer public keys @@ -353,14 +377,39 @@ func (k Keeper) GetPendingConsumerAdditionProp(ctx sdk.Context, spawnTime time.T func (k Keeper) BeginBlockInit(ctx sdk.Context) { propsToExecute := k.GetConsumerAdditionPropsToExecute(ctx) - for _, prop := range propsToExecute { + for i, prop := range propsToExecute { // create consumer client in a cached context to handle errors - cachedCtx, writeFn, err := k.CreateConsumerClientInCachedCtx(ctx, prop) + cachedCtx, writeFn := ctx.CacheContext() + + k.SetTopN(cachedCtx, prop.ChainId, prop.Top_N) + k.SetValidatorSetCap(cachedCtx, prop.ChainId, prop.ValidatorSetCap) + k.SetValidatorsPowerCap(cachedCtx, prop.ChainId, prop.ValidatorsPowerCap) + + for _, address := range prop.Allowlist { + consAddr, err := sdk.ConsAddressFromBech32(address) + if err != nil { + continue + } + + k.SetAllowlist(cachedCtx, prop.ChainId, types.NewProviderConsAddress(consAddr)) + } + + for _, address := range prop.Denylist { + consAddr, err := sdk.ConsAddressFromBech32(address) + if err != nil { + continue + } + + k.SetDenylist(cachedCtx, prop.ChainId, types.NewProviderConsAddress(consAddr)) + } + + err := k.CreateConsumerClient(cachedCtx, &propsToExecute[i]) if err != nil { // drop the proposal ctx.Logger().Info("consumer client could not be created: %w", err) continue } + // The cached context is created with a new EventManager so we merge the event // into the original context ctx.EventManager().EmitEvents(cachedCtx.EventManager().Events()) diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index fc1c7a4344..67b0f93778 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -17,10 +17,11 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // @@ -64,6 +65,11 @@ func TestHandleConsumerAdditionProposal(t *testing.T) { 100000000000, 100000000000, 100000000000, + 0, + 0, + 0, + nil, + nil, ).(*providertypes.ConsumerAdditionProposal), blockTime: now, expAppendProp: true, @@ -89,6 +95,11 @@ func TestHandleConsumerAdditionProposal(t *testing.T) { 100000000000, 100000000000, 100000000000, + 0, + 0, + 0, + nil, + nil, ).(*providertypes.ConsumerAdditionProposal), blockTime: now, expAppendProp: false, @@ -924,6 +935,11 @@ func TestBeginBlockInit(t *testing.T) { 100000000000, 100000000000, 100000000000, + 50, + 0, + 0, + nil, + nil, ).(*providertypes.ConsumerAdditionProposal), providertypes.NewConsumerAdditionProposal( "title", "spawn time passed", "chain2", clienttypes.NewHeight(3, 4), []byte{}, []byte{}, @@ -935,6 +951,11 @@ func TestBeginBlockInit(t *testing.T) { 100000000000, 100000000000, 100000000000, + 50, + 0, + 0, + nil, + nil, ).(*providertypes.ConsumerAdditionProposal), providertypes.NewConsumerAdditionProposal( "title", "spawn time not passed", "chain3", clienttypes.NewHeight(3, 4), []byte{}, []byte{}, @@ -946,6 +967,11 @@ func TestBeginBlockInit(t *testing.T) { 100000000000, 100000000000, 100000000000, + 50, + 0, + 0, + nil, + nil, ).(*providertypes.ConsumerAdditionProposal), providertypes.NewConsumerAdditionProposal( "title", "invalid proposal: chain id already exists", "chain2", clienttypes.NewHeight(4, 5), []byte{}, []byte{}, @@ -957,38 +983,94 @@ func TestBeginBlockInit(t *testing.T) { 100000000000, 100000000000, 100000000000, + 50, + 0, + 0, + nil, + nil, + ).(*providertypes.ConsumerAdditionProposal), + providertypes.NewConsumerAdditionProposal( + "title", "opt-in chain with at least one validator opted in", "chain5", clienttypes.NewHeight(3, 4), []byte{}, []byte{}, + now.Add(-time.Hour*1).UTC(), + "0.75", + 10, + "", + 10000, + 100000000000, + 100000000000, + 100000000000, + 0, + 0, + 0, + nil, + nil, ).(*providertypes.ConsumerAdditionProposal), } - // Expect client creation for only for the 1st and second proposals (spawn time already passed and valid) - gomock.InOrder( - append(testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain1", clienttypes.NewHeight(3, 4)), - testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain2", clienttypes.NewHeight(3, 4))...)..., - ) + // Expect client creation for only the first, second, and fifth proposals (spawn time already passed and valid) + expectedCalls := testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain1", clienttypes.NewHeight(3, 4)) + expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain2", clienttypes.NewHeight(3, 4))...) + expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain5", clienttypes.NewHeight(3, 4))...) + + gomock.InOrder(expectedCalls...) for _, prop := range pendingProps { providerKeeper.SetPendingConsumerAdditionProp(ctx, prop) } + // opt in a sample validator so the chain's proposal can successfully execute + validator := cryptotestutil.NewCryptoIdentityFromIntSeed(0).SDKStakingValidator() + consAddr, _ := validator.GetConsAddr() + providerKeeper.SetOptedIn(ctx, pendingProps[4].ChainId, providertypes.NewProviderConsAddress(consAddr)) + providerKeeper.BeginBlockInit(ctx) - // Only the third proposal is still stored as pending + // first proposal is not pending anymore because its spawn time already passed and was executed _, found := providerKeeper.GetPendingConsumerAdditionProp( ctx, pendingProps[0].SpawnTime, pendingProps[0].ChainId) require.False(t, found) + // first proposal was successfully executed and hence consumer genesis was created + _, found = providerKeeper.GetConsumerGenesis(ctx, pendingProps[0].ChainId) + require.True(t, found) + // second proposal is not pending anymore because its spawn time already passed and was executed _, found = providerKeeper.GetPendingConsumerAdditionProp( ctx, pendingProps[1].SpawnTime, pendingProps[1].ChainId) require.False(t, found) + // second proposal was successfully executed and hence consumer genesis was created + _, found = providerKeeper.GetConsumerGenesis(ctx, pendingProps[1].ChainId) + require.True(t, found) + // third proposal is still stored as pending because its spawn time has not passed _, found = providerKeeper.GetPendingConsumerAdditionProp( ctx, pendingProps[2].SpawnTime, pendingProps[2].ChainId) require.True(t, found) + // because the proposal is still pending, no consumer genesis was created + _, found = providerKeeper.GetConsumerGenesis(ctx, pendingProps[2].ChainId) + require.False(t, found) - // check that the invalid proposal was dropped + // check that the invalid proposals were dropped _, found = providerKeeper.GetPendingConsumerAdditionProp( ctx, pendingProps[3].SpawnTime, pendingProps[3].ChainId) require.False(t, found) + // Note that we do not check that `GetConsumerGenesis(ctx, pendingProps[3].ChainId)` returns `false` here because + // `pendingProps[3]` is an invalid proposal due to the chain id already existing so the consumer genesis also exists + + // fifth proposal corresponds to an Opt-In chain with one opted-in validator and hence the proposal gets + // successfully executed + _, found = providerKeeper.GetPendingConsumerAdditionProp( + ctx, pendingProps[4].SpawnTime, pendingProps[4].ChainId) + require.False(t, found) + // sixth proposal was successfully executed and hence consumer genesis was created + _, found = providerKeeper.GetConsumerGenesis(ctx, pendingProps[4].ChainId) + require.True(t, found) + + // test that Top N is set correctly + require.True(t, providerKeeper.IsTopN(ctx, "chain1")) + topN, found := providerKeeper.GetTopN(ctx, "chain1") + require.Equal(t, uint32(50), topN) + + require.True(t, providerKeeper.IsOptIn(ctx, "chain4")) } // TestBeginBlockCCR tests BeginBlockCCR against the spec. @@ -1040,6 +1122,7 @@ func TestBeginBlockCCR(t *testing.T) { additionProp := testkeeper.GetTestConsumerAdditionProp() additionProp.ChainId = prop.ChainId additionProp.InitialHeight = clienttypes.NewHeight(2, 3) + err := providerKeeper.CreateConsumerClient(ctx, additionProp) require.NoError(t, err) err = providerKeeper.SetConsumerChain(ctx, "channelID") @@ -1058,6 +1141,7 @@ func TestBeginBlockCCR(t *testing.T) { // // Test execution // + providerKeeper.BeginBlockCCR(ctx) // Only the 3rd (final) proposal is still stored as pending diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index 939f6d3995..f785d5d6b5 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -12,8 +12,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // OnRecvVSCMaturedPacket handles a VSCMatured packet and returns a no-op result ack. @@ -222,7 +222,17 @@ func (k Keeper) QueueVSCPackets(ctx sdk.Context) { for _, chain := range k.GetAllConsumerChains(ctx) { currentValidators := k.GetConsumerValSet(ctx, chain.ChainId) - nextValidators := k.ComputeNextEpochConsumerValSet(ctx, chain.ChainId, bondedValidators) + + if topN, found := k.GetTopN(ctx, chain.ChainId); found && topN > 0 { + // in a Top-N chain, we automatically opt in all validators that belong to the top N + minPower, err := k.ComputeMinPowerToOptIn(ctx, chain.ChainId, bondedValidators, topN) + if err == nil { + k.OptInTopNValidators(ctx, chain.ChainId, bondedValidators, minPower) + } + } + + nextValidators := k.ComputeNextValidators(ctx, chain.ChainId, bondedValidators) + valUpdates := DiffValidators(currentValidators, nextValidators) k.SetConsumerValSet(ctx, chain.ChainId, nextValidators) @@ -328,6 +338,16 @@ func (k Keeper) OnRecvSlashPacket( return ccv.V1Result, nil } + // Check that the validator belongs to the consumer chain valset + if !k.IsConsumerValidator(ctx, chainID, providerConsAddr) { + k.Logger(ctx).Error("cannot jail validator %s that does not belong to consumer %s valset", + providerConsAddr.String(), chainID) + // drop packet but return a slash ack so that the consumer can send another slash packet + k.AppendSlashAck(ctx, chainID, consumerConsAddr.String()) + + return ccv.SlashPacketHandledResult, nil + } + meter := k.GetSlashMeter(ctx) // Return bounce ack if meter is negative in value if meter.IsNegative() { diff --git a/x/ccv/provider/keeper/relay_test.go b/x/ccv/provider/keeper/relay_test.go index 1e7347d3ce..2b50c695bc 100644 --- a/x/ccv/provider/keeper/relay_test.go +++ b/x/ccv/provider/keeper/relay_test.go @@ -12,17 +12,21 @@ import ( "cosmossdk.io/math" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" abci "github.com/cometbft/cometbft/abci/types" - cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/testutil/crypto" + cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // TestQueueVSCPackets tests queueing validator set updates. @@ -121,12 +125,22 @@ func TestOnRecvDowntimeSlashPacket(t *testing.T) { // Set a block height for the valset update id in the generated packet data providerKeeper.SetValsetUpdateBlockHeight(ctx, packetData.ValsetUpdateId, uint64(15)) + // Set consumer validator + providerKeeper.SetConsumerValidator(ctx, "chain-1", providertypes.ConsumerValidator{ + ProviderConsAddr: packetData.Validator.Address, + }) + // Set slash meter to negative value and assert a bounce ack is returned providerKeeper.SetSlashMeter(ctx, math.NewInt(-5)) ackResult, err := executeOnRecvSlashPacket(t, &providerKeeper, ctx, "channel-1", 1, packetData) require.Equal(t, ccv.SlashPacketBouncedResult, ackResult) require.NoError(t, err) + // Set consumer validator + providerKeeper.SetConsumerValidator(ctx, "chain-2", providertypes.ConsumerValidator{ + ProviderConsAddr: packetData.Validator.Address, + }) + // Also bounced for chain-2 ackResult, err = executeOnRecvSlashPacket(t, &providerKeeper, ctx, "channel-2", 2, packetData) require.Equal(t, ccv.SlashPacketBouncedResult, ackResult) @@ -135,6 +149,9 @@ func TestOnRecvDowntimeSlashPacket(t *testing.T) { // Now set slash meter to positive value and assert slash packet handled result is returned providerKeeper.SetSlashMeter(ctx, math.NewInt(5)) + // Set the consumer validator + providerKeeper.SetConsumerValidator(ctx, "chain-1", types.ConsumerValidator{ProviderConsAddr: packetData.Validator.Address}) + // Mock call to GetEffectiveValPower, so that it returns 2. providerAddr := providertypes.NewProviderConsAddress(packetData.Validator.Address) calls := []*gomock.Call{ @@ -288,6 +305,7 @@ func TestValidateSlashPacket(t *testing.T) { func TestHandleSlashPacket(t *testing.T) { chainId := "consumer-id" validVscID := uint64(234) + providerConsAddr := cryptotestutil.NewCryptoIdentityFromIntSeed(7842334).ProviderConsAddress() consumerConsAddr := cryptotestutil.NewCryptoIdentityFromIntSeed(784987634).ConsumerConsAddress() @@ -295,8 +313,9 @@ func TestHandleSlashPacket(t *testing.T) { name string packetData ccv.SlashPacketData // The mocks that we expect to be called for the specified packet data. - expectedCalls func(sdk.Context, testkeeper.MockedKeepers, ccv.SlashPacketData) []*gomock.Call - expectedSlashAcksLen int + expectedCalls func(sdk.Context, testkeeper.MockedKeepers, ccv.SlashPacketData) []*gomock.Call + expectedSlashAcksLen int + expectedSlashAckConsumerConsAddress types.ConsumerConsAddress }{ { "unfound validator", @@ -318,6 +337,7 @@ func TestHandleSlashPacket(t *testing.T) { } }, 0, + consumerConsAddr, }, { "found, but tombstoned validator", @@ -340,6 +360,7 @@ func TestHandleSlashPacket(t *testing.T) { } }, 0, + consumerConsAddr, }, { "drop packet when infraction height not found", @@ -363,6 +384,7 @@ func TestHandleSlashPacket(t *testing.T) { } }, 0, + consumerConsAddr, }, { "full downtime packet handling, uses init chain height and non-jailed validator", @@ -380,6 +402,7 @@ func TestHandleSlashPacket(t *testing.T) { true) // expectJailing = true }, 1, + consumerConsAddr, }, { "full downtime packet handling, uses valid vscID and jailed validator", @@ -397,39 +420,42 @@ func TestHandleSlashPacket(t *testing.T) { false) // expectJailing = false, validator is already jailed. }, 1, + consumerConsAddr, }, // Note: double-sign slash packet handling should not occur, see OnRecvSlashPacket. } for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx( + t, testkeeper.NewInMemKeeperParams(t)) - providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx( - t, testkeeper.NewInMemKeeperParams(t)) - - // Setup expected mock calls - gomock.InOrder(tc.expectedCalls(ctx, mocks, tc.packetData)...) + // Setup expected mock calls + gomock.InOrder(tc.expectedCalls(ctx, mocks, tc.packetData)...) - // Setup init chain height and a single valid valset update ID to block height mapping. - providerKeeper.SetInitChainHeight(ctx, chainId, 5) - providerKeeper.SetValsetUpdateBlockHeight(ctx, validVscID, 99) + // Setup init chain height and a single valid valset update ID to block height mapping. + providerKeeper.SetInitChainHeight(ctx, chainId, 5) + providerKeeper.SetValsetUpdateBlockHeight(ctx, validVscID, 99) - // Setup consumer address to provider address mapping. - require.NotEmpty(t, tc.packetData.Validator.Address) - providerKeeper.SetValidatorByConsumerAddr(ctx, chainId, consumerConsAddr, providerConsAddr) + // Setup consumer address to provider address mapping. + require.NotEmpty(t, tc.packetData.Validator.Address) + providerKeeper.SetValidatorByConsumerAddr(ctx, chainId, consumerConsAddr, providerConsAddr) + providerKeeper.SetConsumerValidator(ctx, chainId, types.ConsumerValidator{ProviderConsAddr: providerConsAddr.Address.Bytes()}) - // Execute method and assert expected mock calls. - providerKeeper.HandleSlashPacket(ctx, chainId, tc.packetData) + // Execute method and assert expected mock calls. + providerKeeper.HandleSlashPacket(ctx, chainId, tc.packetData) - require.Equal(t, tc.expectedSlashAcksLen, len(providerKeeper.GetSlashAcks(ctx, chainId))) + require.Equal(t, tc.expectedSlashAcksLen, len(providerKeeper.GetSlashAcks(ctx, chainId))) - if tc.expectedSlashAcksLen == 1 { - // must match the consumer address - require.Equal(t, consumerConsAddr.String(), providerKeeper.GetSlashAcks(ctx, chainId)[0]) - require.NotEqual(t, providerConsAddr.String(), providerKeeper.GetSlashAcks(ctx, chainId)[0]) - require.NotEqual(t, providerConsAddr.String(), consumerConsAddr.String()) - } + if tc.expectedSlashAcksLen == 1 { + // must match the consumer address + require.Equal(t, tc.expectedSlashAckConsumerConsAddress.String(), providerKeeper.GetSlashAcks(ctx, chainId)[0]) + require.NotEqual(t, providerConsAddr.String(), providerKeeper.GetSlashAcks(ctx, chainId)[0]) + require.NotEqual(t, providerConsAddr.String(), consumerConsAddr.String()) + } - ctrl.Finish() + ctrl.Finish() + }) } } @@ -444,6 +470,10 @@ func TestHandleVSCMaturedPacket(t *testing.T) { // Start first unbonding without any consumers registered var unbondingOpId uint64 = 1 + gomock.InOrder( + mocks.MockStakingKeeper.EXPECT().GetUnbondingType(ctx, unbondingOpId).Return(stakingtypes.UnbondingType_Undefined, false), + ) + err := pk.Hooks().AfterUnbondingInitiated(ctx, unbondingOpId) require.NoError(t, err) // Check that no unbonding op was stored @@ -454,12 +484,34 @@ func TestHandleVSCMaturedPacket(t *testing.T) { pk.IncrementValidatorSetUpdateId(ctx) require.Equal(t, uint64(2), pk.GetValidatorSetUpdateId(ctx)) - // Registered first consumer + // Register first consumer pk.SetConsumerClientId(ctx, "chain-1", "client-1") + // Create 2 validators + vals := []stakingtypes.Validator{} + valsPk := []cryptotypes.PubKey{} + for i := 0; i < 2; i++ { + pubkey, err := cryptocodec.FromTmPubKeyInterface(cryptotestutil.NewCryptoIdentityFromIntSeed(54321 + i).TMCryptoPubKey()) + require.NoError(t, err) + valsPk = append(valsPk, pubkey) + pkAny, err := codectypes.NewAnyWithValue(pubkey) + require.NoError(t, err) + vals = append(vals, stakingtypes.Validator{ConsensusPubkey: pkAny}) + } + + // Opt-in one validator to consumer + pk.SetConsumerValidator(ctx, "chain-1", types.ConsumerValidator{ProviderConsAddr: valsPk[0].Address()}) + // Start second unbonding unbondingOpId = 2 gomock.InOrder( + mocks.MockStakingKeeper.EXPECT().GetUnbondingType(ctx, unbondingOpId).Return(stakingtypes.UnbondingType_UnbondingDelegation, true), + mocks.MockStakingKeeper.EXPECT().GetUnbondingDelegationByUnbondingID(ctx, unbondingOpId).Return( + stakingtypes.UnbondingDelegation{ + ValidatorAddress: sdk.ValAddress([]byte{1}).String(), + }, true), + mocks.MockStakingKeeper.EXPECT().GetValidator(ctx, sdk.ValAddress([]byte{1})). + Return(vals[0], true), mocks.MockStakingKeeper.EXPECT().PutUnbondingOnHold(ctx, unbondingOpId).Return(nil), ) err = pk.Hooks().AfterUnbondingInitiated(ctx, unbondingOpId) @@ -483,10 +535,21 @@ func TestHandleVSCMaturedPacket(t *testing.T) { // Registered second consumer pk.SetConsumerClientId(ctx, "chain-2", "client-2") + // Opt-in both validators to second consumer + pk.SetConsumerValidator(ctx, "chain-2", types.ConsumerValidator{ProviderConsAddr: valsPk[0].Address()}) + pk.SetConsumerValidator(ctx, "chain-2", types.ConsumerValidator{ProviderConsAddr: valsPk[1].Address()}) + // Start third and fourth unbonding unbondingOpIds := []uint64{3, 4} for _, id := range unbondingOpIds { gomock.InOrder( + mocks.MockStakingKeeper.EXPECT().GetUnbondingType(ctx, id).Return(stakingtypes.UnbondingType_Redelegation, true), + mocks.MockStakingKeeper.EXPECT().GetRedelegationByUnbondingID(ctx, id).Return( + stakingtypes.Redelegation{ + ValidatorSrcAddress: sdk.ValAddress([]byte{1}).String(), + }, true), + mocks.MockStakingKeeper.EXPECT().GetValidator(ctx, sdk.ValAddress([]byte{1})). + Return(vals[0], true), mocks.MockStakingKeeper.EXPECT().PutUnbondingOnHold(ctx, id).Return(nil), ) err = pk.Hooks().AfterUnbondingInitiated(ctx, id) @@ -507,6 +570,33 @@ func TestHandleVSCMaturedPacket(t *testing.T) { require.Equal(t, unbondingOpIds, ids) } + // Increment vscID + pk.IncrementValidatorSetUpdateId(ctx) + require.Equal(t, uint64(4), pk.GetValidatorSetUpdateId(ctx)) + + // Start fith unbonding + unbondingOpId = 5 + gomock.InOrder( + mocks.MockStakingKeeper.EXPECT().GetUnbondingType(ctx, unbondingOpId).Return(stakingtypes.UnbondingType_ValidatorUnbonding, true), + mocks.MockStakingKeeper.EXPECT().GetValidatorByUnbondingID(ctx, unbondingOpId).Return( + stakingtypes.Validator{ + OperatorAddress: sdk.ValAddress([]byte{1}).String(), + }, true), + mocks.MockStakingKeeper.EXPECT().GetValidator(ctx, sdk.ValAddress([]byte{1})). + Return(vals[1], true), + mocks.MockStakingKeeper.EXPECT().PutUnbondingOnHold(ctx, unbondingOpId).Return(nil), + ) + err = pk.Hooks().AfterUnbondingInitiated(ctx, unbondingOpId) + require.NoError(t, err) + + // Check that an unbonding op was stored for chain-2 only + // since it's the only consumer the unbonding validator has opted-in to + expectedChains = []string{"chain-2"} + unbondingOp, found = pk.GetUnbondingOp(ctx, unbondingOpId) + require.True(t, found) + require.Equal(t, unbondingOpId, unbondingOp.Id) + require.Equal(t, expectedChains, unbondingOp.UnbondingConsumerChains) + // Handle VSCMatured packet from chain-1 for vscID 1. // Note that no VSCPacket was sent as the chain was not yet registered, // but the code should still work @@ -658,6 +748,10 @@ func TestEndBlockVSU(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() + chainID := "chainID" + + providerKeeper.SetTopN(ctx, chainID, 100) + // 10 blocks constitute an epoch params := providertypes.DefaultParams() params.BlocksPerEpoch = 10 @@ -665,34 +759,116 @@ func TestEndBlockVSU(t *testing.T) { // create 4 sample lastValidators var lastValidators []stakingtypes.Validator + var valAddresses []sdk.ValAddress for i := 0; i < 4; i++ { - lastValidators = append(lastValidators, cryptotestutil.NewCryptoIdentityFromIntSeed(i).SDKStakingValidator()) + validator := crypto.NewCryptoIdentityFromIntSeed(i).SDKStakingValidator() + lastValidators = append(lastValidators, validator) + valAddresses = append(valAddresses, validator.GetOperator()) + mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), validator.GetOperator()).Return(int64(i + 1)).AnyTimes() } + mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Return(lastValidators).AnyTimes() - mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), gomock.Any()).Return(int64(2)).AnyTimes() // set a sample client for a consumer chain so that `GetAllConsumerChains` in `QueueVSCPackets` iterates at least once - providerKeeper.SetConsumerClientId(ctx, "chainID", "clientID") + providerKeeper.SetConsumerClientId(ctx, chainID, "clientID") // with block height of 1 we do not expect any queueing of VSC packets ctx = ctx.WithBlockHeight(1) providerKeeper.EndBlockVSU(ctx) - require.Equal(t, 0, len(providerKeeper.GetPendingVSCPackets(ctx, "chainID"))) + require.Equal(t, 0, len(providerKeeper.GetPendingVSCPackets(ctx, chainID))) // with block height of 5 we do not expect any queueing of VSC packets ctx = ctx.WithBlockHeight(5) providerKeeper.EndBlockVSU(ctx) - require.Equal(t, 0, len(providerKeeper.GetPendingVSCPackets(ctx, "chainID"))) + require.Equal(t, 0, len(providerKeeper.GetPendingVSCPackets(ctx, chainID))) // with block height of 10 we expect the queueing of one VSC packet ctx = ctx.WithBlockHeight(10) providerKeeper.EndBlockVSU(ctx) - require.Equal(t, 1, len(providerKeeper.GetPendingVSCPackets(ctx, "chainID"))) + require.Equal(t, 1, len(providerKeeper.GetPendingVSCPackets(ctx, chainID))) // With block height of 15 we expect no additional queueing of a VSC packet. // Note that the pending VSC packet is still there because `SendVSCPackets` does not send the packet. We // need to mock channels, etc. for this to work, and it's out of scope for this test. ctx = ctx.WithBlockHeight(15) providerKeeper.EndBlockVSU(ctx) - require.Equal(t, 1, len(providerKeeper.GetPendingVSCPackets(ctx, "chainID"))) + require.Equal(t, 1, len(providerKeeper.GetPendingVSCPackets(ctx, chainID))) +} + +// TestQueueVSCPacketsWithPowerCapping tests queueing validator set updates with power capping +func TestQueueVSCPacketsWithPowerCapping(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + providerKeeper.SetValidatorSetUpdateId(ctx, 1) + + valA := createStakingValidator(ctx, mocks, 1, 1) // 3.125% of the total voting power + valAConsAddr, _ := valA.GetConsAddr() + valAPubKey, _ := valA.TmConsPublicKey() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valAConsAddr).Return(valA, true).AnyTimes() + valB := createStakingValidator(ctx, mocks, 2, 3) // 9.375% of the total voting power + valBConsAddr, _ := valB.GetConsAddr() + valBPubKey, _ := valB.TmConsPublicKey() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valBConsAddr).Return(valB, true).AnyTimes() + valC := createStakingValidator(ctx, mocks, 3, 4) // 12.5% of the total voting power + valCConsAddr, _ := valC.GetConsAddr() + valCPubKey, _ := valC.TmConsPublicKey() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valCConsAddr).Return(valC, true).AnyTimes() + valD := createStakingValidator(ctx, mocks, 4, 8) // 25% of the total voting power + valDConsAddr, _ := valD.GetConsAddr() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valDConsAddr).Return(valD, true).AnyTimes() + valE := createStakingValidator(ctx, mocks, 5, 16) // 50% of the total voting power + valEConsAddr, _ := valE.GetConsAddr() + valEPubKey, _ := valE.TmConsPublicKey() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valEConsAddr).Return(valE, true).AnyTimes() + + mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Return([]stakingtypes.Validator{valA, valB, valC, valD, valE}).AnyTimes() + + // add a consumer chain + providerKeeper.SetConsumerClientId(ctx, "chainID", "clientID") + + providerKeeper.SetTopN(ctx, "chainID", 50) // would opt in E + + // opt in all validators + providerKeeper.SetOptedIn(ctx, "chainID", providertypes.NewProviderConsAddress(valAConsAddr)) + providerKeeper.SetOptedIn(ctx, "chainID", providertypes.NewProviderConsAddress(valBConsAddr)) + providerKeeper.SetOptedIn(ctx, "chainID", providertypes.NewProviderConsAddress(valCConsAddr)) + providerKeeper.SetOptedIn(ctx, "chainID", providertypes.NewProviderConsAddress(valDConsAddr)) + + // denylist validator D + providerKeeper.SetDenylist(ctx, "chainID", providertypes.NewProviderConsAddress(valDConsAddr)) + + // set a power-capping of 40% + providerKeeper.SetValidatorsPowerCap(ctx, "chainID", 40) + + providerKeeper.QueueVSCPackets(ctx) + + actualQueuedVSCPackets := providerKeeper.GetPendingVSCPackets(ctx, "chainID") + expectedQueuedVSCPackets := []ccv.ValidatorSetChangePacketData{ + ccv.NewValidatorSetChangePacketData( + []abci.ValidatorUpdate{ + // validator D is not here because it was denylisted + // powers have changed because of power capping + { + PubKey: valEPubKey, + Power: 9, + }, + { + PubKey: valCPubKey, + Power: 6, + }, + { + PubKey: valBPubKey, + Power: 5, + }, + { + PubKey: valAPubKey, + Power: 4, + }, + }, + 1, + nil), + } + + require.Equal(t, expectedQueuedVSCPackets, actualQueuedVSCPackets) } diff --git a/x/ccv/provider/keeper/throttle.go b/x/ccv/provider/keeper/throttle.go index b7e7fd5941..b1da78db0f 100644 --- a/x/ccv/provider/keeper/throttle.go +++ b/x/ccv/provider/keeper/throttle.go @@ -10,7 +10,7 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) // Obtains the effective validator power relevant to a validator consensus address. diff --git a/x/ccv/provider/keeper/throttle_legacy.go b/x/ccv/provider/keeper/throttle_legacy.go index 6f347b8f60..c14c994e84 100644 --- a/x/ccv/provider/keeper/throttle_legacy.go +++ b/x/ccv/provider/keeper/throttle_legacy.go @@ -5,8 +5,8 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // Pending packet data type enum, used to encode the type of packet data stored at each entry in the mutual queue. diff --git a/x/ccv/provider/keeper/throttle_test.go b/x/ccv/provider/keeper/throttle_test.go index 478ec0f235..5a315e5ec9 100644 --- a/x/ccv/provider/keeper/throttle_test.go +++ b/x/ccv/provider/keeper/throttle_test.go @@ -13,8 +13,8 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) // TestSlashMeterReplenishment tests the CheckForSlashMeterReplenishment, ReplenishSlashMeter, diff --git a/x/ccv/provider/keeper/validator_set_update.go b/x/ccv/provider/keeper/validator_set_update.go index adaee853e8..42016708a5 100644 --- a/x/ccv/provider/keeper/validator_set_update.go +++ b/x/ccv/provider/keeper/validator_set_update.go @@ -8,7 +8,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) // SetConsumerValidator sets provided consumer `validator` on the consumer chain with `chainID` @@ -26,6 +26,15 @@ func (k Keeper) SetConsumerValidator( store.Set(types.ConsumerValidatorKey(chainID, validator.ProviderConsAddr), bz) } +// SetConsumerValSet resets the current consumer validators with the `nextValidators` computed by +// `FilterValidators` and hence this method should only be called after `FilterValidators` has completed. +func (k Keeper) SetConsumerValSet(ctx sdk.Context, chainID string, nextValidators []types.ConsumerValidator) { + k.DeleteConsumerValSet(ctx, chainID) + for _, val := range nextValidators { + k.SetConsumerValidator(ctx, chainID, val) + } +} + // DeleteConsumerValidator removes consumer validator with `providerAddr` address func (k Keeper) DeleteConsumerValidator( ctx sdk.Context, @@ -57,11 +66,7 @@ func (k Keeper) DeleteConsumerValSet( // IsConsumerValidator returns `true` if the consumer validator with `providerAddr` exists for chain `chainID` // and `false` otherwise -func (k Keeper) IsConsumerValidator( - ctx sdk.Context, - chainID string, - providerAddr types.ProviderConsAddress, -) bool { +func (k Keeper) IsConsumerValidator(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress) bool { store := ctx.KVStore(k.storeKey) return store.Get(types.ConsumerValidatorKey(chainID, providerAddr.ToSdkConsAddr())) != nil } @@ -88,51 +93,6 @@ func (k Keeper) GetConsumerValSet( return validators } -// ComputeNextEpochConsumerValSet returns the next validator set that is responsible for validating consumer -// chain `chainID`, based on the bonded validators. -func (k Keeper) ComputeNextEpochConsumerValSet( - ctx sdk.Context, - chainID string, - bondedValidators []stakingtypes.Validator, -) []types.ConsumerValidator { - var nextValidators []types.ConsumerValidator - for _, val := range bondedValidators { - // get next voting power and the next consumer public key - nextPower := k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()) - consAddr, err := val.GetConsAddr() - if err != nil { - // this should never happen but is recoverable if we exclude this validator from the `nextValidators` - k.Logger(ctx).Error("could not get consensus address of validator", - "validator", val.GetOperator().String(), - "error", err) - continue - } - nextConsumerPublicKey, foundConsumerPublicKey := k.GetValidatorConsumerPubKey(ctx, chainID, types.NewProviderConsAddress(consAddr)) - if !foundConsumerPublicKey { - // if no consumer key assigned then use the validator's key itself - k.Logger(ctx).Info("could not retrieve public key for validator on consumer chain because"+ - " the validator did not assign a new consumer key", - "validator", val.GetOperator().String(), - "chainID", chainID) - nextConsumerPublicKey, err = val.TmConsPublicKey() - if err != nil { - // this should never happen and might not be recoverable because without the public key - // we cannot generate a validator update - panic(fmt.Errorf("could not retrieve validator's (%+v) public key: %w", val, err)) - } - } - - nextValidator := types.ConsumerValidator{ - ProviderConsAddr: consAddr, - Power: nextPower, - ConsumerPublicKey: &nextConsumerPublicKey, - } - nextValidators = append(nextValidators, nextValidator) - } - - return nextValidators -} - // DiffValidators compares the current and the next epoch's consumer validators and returns the `ValidatorUpdate` diff // needed by CometBFT to update the validator set on a chain. func DiffValidators( @@ -174,11 +134,58 @@ func DiffValidators( return updates } -// SetConsumerValSet resets the current consumer validators with the `nextValidators` computed by -// `ComputeNextEpochConsumerValSet` and hence this method should only be called after `ComputeNextEpochConsumerValSet` has completed. -func (k Keeper) SetConsumerValSet(ctx sdk.Context, chainID string, nextValidators []types.ConsumerValidator) { - k.DeleteConsumerValSet(ctx, chainID) - for _, val := range nextValidators { - k.SetConsumerValidator(ctx, chainID, val) +// CreateConsumerValidator creates a consumer validator for `chainID` from the given staking `validator` +func (k Keeper) CreateConsumerValidator(ctx sdk.Context, chainID string, validator stakingtypes.Validator) (types.ConsumerValidator, error) { + power := k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()) + consAddr, err := validator.GetConsAddr() + if err != nil { + return types.ConsumerValidator{}, fmt.Errorf("could not retrieve validator's (%+v) consensus address: %w", + validator, err) + } + + consumerPublicKey, foundConsumerPublicKey := k.GetValidatorConsumerPubKey(ctx, chainID, types.NewProviderConsAddress(consAddr)) + if !foundConsumerPublicKey { + consumerPublicKey, err = validator.TmConsPublicKey() + if err != nil { + return types.ConsumerValidator{}, fmt.Errorf("could not retrieve validator's (%+v) public key: %w", validator, err) + } + } + + return types.ConsumerValidator{ + ProviderConsAddr: consAddr, + Power: power, + ConsumerPublicKey: &consumerPublicKey, + }, nil +} + +// FilterValidators filters the provided `bondedValidators` according to `predicate` and returns +// the filtered set. +func (k Keeper) FilterValidators( + ctx sdk.Context, + chainID string, + bondedValidators []stakingtypes.Validator, + predicate func(providerAddr types.ProviderConsAddress) bool, +) []types.ConsumerValidator { + var nextValidators []types.ConsumerValidator + for _, val := range bondedValidators { + consAddr, err := val.GetConsAddr() + if err != nil { + continue + } + + if predicate(types.NewProviderConsAddress(consAddr)) { + nextValidator, err := k.CreateConsumerValidator(ctx, chainID, val) + if err != nil { + // this should never happen but is recoverable if we exclude this validator from the next validator set + k.Logger(ctx).Error("could not create consumer validator", + "validator", val.GetOperator().String(), + "error", err) + continue + } + + nextValidators = append(nextValidators, nextValidator) + } } + + return nextValidators } diff --git a/x/ccv/provider/keeper/validator_set_update_test.go b/x/ccv/provider/keeper/validator_set_update_test.go index 3c6441bbc5..9da3039b14 100644 --- a/x/ccv/provider/keeper/validator_set_update_test.go +++ b/x/ccv/provider/keeper/validator_set_update_test.go @@ -16,10 +16,10 @@ import ( "github.com/cometbft/cometbft/crypto/ed25519" "github.com/cometbft/cometbft/proto/tendermint/crypto" - cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) // TestConsumerValidator tests the `SetConsumerValidator`, `IsConsumerValidator`, and `DeleteConsumerValidator` methods @@ -110,62 +110,22 @@ func createConsumerValidator(index int, power int64, seed int) (types.ConsumerVa }, publicKey } -func TestComputeNextEpochConsumerValSet(t *testing.T) { - providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - chainID := "chainID" - - // helper function to generate a validator with the given power and with a provider address based on index - createStakingValidator := func(ctx sdk.Context, mocks testkeeper.MockedKeepers, index int, power int64) stakingtypes.Validator { - providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{byte(index)}).PubKey() - consAddr := sdk.ConsAddress(providerConsPubKey.Address()) - providerAddr := types.NewProviderConsAddress(consAddr) - pk, _ := cryptocodec.FromTmPubKeyInterface(providerConsPubKey) - pkAny, _ := codectypes.NewAnyWithValue(pk) - - var providerValidatorAddr sdk.ValAddress = providerAddr.Address.Bytes() - - mocks.MockStakingKeeper.EXPECT(). - GetLastValidatorPower(ctx, providerValidatorAddr).Return(power).AnyTimes() - - return stakingtypes.Validator{ - OperatorAddress: providerValidatorAddr.String(), - ConsensusPubkey: pkAny, - } +// createStakingValidator helper function to generate a validator with the given power and with a provider address based on index +func createStakingValidator(ctx sdk.Context, mocks testkeeper.MockedKeepers, index int, power int64) stakingtypes.Validator { + providerConsPubKey := ed25519.GenPrivKeyFromSecret([]byte{byte(index)}).PubKey() + consAddr := sdk.ConsAddress(providerConsPubKey.Address()) + providerAddr := types.NewProviderConsAddress(consAddr) + pk, _ := cryptocodec.FromTmPubKeyInterface(providerConsPubKey) + pkAny, _ := codectypes.NewAnyWithValue(pk) + providerValidatorAddr := sdk.ValAddress(providerAddr.Address.Bytes()) + + mocks.MockStakingKeeper.EXPECT(). + GetLastValidatorPower(ctx, providerValidatorAddr).Return(power).AnyTimes() + + return stakingtypes.Validator{ + OperatorAddress: providerValidatorAddr.String(), + ConsensusPubkey: pkAny, } - - // no consumer validators returned if we have no bonded validators - require.Empty(t, providerKeeper.ComputeNextEpochConsumerValSet(ctx, chainID, []stakingtypes.Validator{})) - - var expectedValidators []types.ConsumerValidator - - // create a staking validator A that has not set a consumer public key - valA := createStakingValidator(ctx, mocks, 1, 1) - // because validator A has no consumer key set, the `ConsumerPublicKey` we expect is the key on the provider chain - valAConsAddr, _ := valA.GetConsAddr() - valAPublicKey, _ := valA.TmConsPublicKey() - expectedValidators = append(expectedValidators, types.ConsumerValidator{ - ProviderConsAddr: types.NewProviderConsAddress(valAConsAddr).Address.Bytes(), - Power: 1, - ConsumerPublicKey: &valAPublicKey, - }) - - // create a staking validator B that has set a consumer public key - valB := createStakingValidator(ctx, mocks, 2, 2) - // validator B has set a consumer key, the `ConsumerPublicKey` we expect is the key set by `SetValidatorConsumerPubKey` - valBConsumerKey := cryptotestutil.NewCryptoIdentityFromIntSeed(1).TMProtoCryptoPublicKey() - valBConsAddr, _ := valB.GetConsAddr() - providerKeeper.SetValidatorConsumerPubKey(ctx, chainID, types.NewProviderConsAddress(valBConsAddr), valBConsumerKey) - expectedValidators = append(expectedValidators, types.ConsumerValidator{ - ProviderConsAddr: types.NewProviderConsAddress(valBConsAddr).Address.Bytes(), - Power: 2, - ConsumerPublicKey: &valBConsumerKey, - }) - - bondedValidators := []stakingtypes.Validator{valA, valB} - actualValidators := providerKeeper.ComputeNextEpochConsumerValSet(ctx, "chainID", bondedValidators) - require.Equal(t, expectedValidators, actualValidators) } func TestDiff(t *testing.T) { @@ -366,3 +326,153 @@ func TestSetConsumerValSet(t *testing.T) { sortValidators(nextCurrentValidators) require.Equal(t, nextValidators, nextCurrentValidators) } + +func TestComputeNextEpochConsumerValSetConsiderAll(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainID := "chainID" + + // no consumer validators returned if we have no bonded validators + considerAll := func(providerAddr types.ProviderConsAddress) bool { return true } + require.Empty(t, providerKeeper.FilterValidators(ctx, chainID, []stakingtypes.Validator{}, considerAll)) + + var expectedValidators []types.ConsumerValidator + + // create a staking validator A that has not set a consumer public key + valA := createStakingValidator(ctx, mocks, 1, 1) + // because validator A has no consumer key set, the `ConsumerPublicKey` we expect is the key on the provider chain + valAConsAddr, _ := valA.GetConsAddr() + valAPublicKey, _ := valA.TmConsPublicKey() + expectedValidators = append(expectedValidators, types.ConsumerValidator{ + ProviderConsAddr: types.NewProviderConsAddress(valAConsAddr).Address.Bytes(), + Power: 1, + ConsumerPublicKey: &valAPublicKey, + }) + + // create a staking validator B that has set a consumer public key + valB := createStakingValidator(ctx, mocks, 2, 2) + // validator B has set a consumer key, the `ConsumerPublicKey` we expect is the key set by `SetValidatorConsumerPubKey` + valBConsumerKey := cryptotestutil.NewCryptoIdentityFromIntSeed(1).TMProtoCryptoPublicKey() + valBConsAddr, _ := valB.GetConsAddr() + providerKeeper.SetValidatorConsumerPubKey(ctx, chainID, types.NewProviderConsAddress(valBConsAddr), valBConsumerKey) + expectedValidators = append(expectedValidators, types.ConsumerValidator{ + ProviderConsAddr: types.NewProviderConsAddress(valBConsAddr).Address.Bytes(), + Power: 2, + ConsumerPublicKey: &valBConsumerKey, + }) + + bondedValidators := []stakingtypes.Validator{valA, valB} + actualValidators := providerKeeper.FilterValidators(ctx, chainID, bondedValidators, considerAll) + require.Equal(t, expectedValidators, actualValidators) +} + +func TestComputeNextEpochConsumerValSetConsiderOnlyOptIn(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainID := "chainID" + + // no consumer validators returned if we have no opted-in validators + require.Empty(t, providerKeeper.FilterValidators(ctx, chainID, []stakingtypes.Validator{}, + func(providerAddr types.ProviderConsAddress) bool { + return providerKeeper.IsOptedIn(ctx, chainID, providerAddr) + })) + + var expectedValidators []types.ConsumerValidator + + // create a staking validator A that has not set a consumer public key + valA := createStakingValidator(ctx, mocks, 1, 1) + // because validator A has no consumer key set, the `ConsumerPublicKey` we expect is the key on the provider chain + valAConsAddr, _ := valA.GetConsAddr() + valAPublicKey, _ := valA.TmConsPublicKey() + expectedValAConsumerValidator := types.ConsumerValidator{ + ProviderConsAddr: types.NewProviderConsAddress(valAConsAddr).Address.Bytes(), + Power: 1, + ConsumerPublicKey: &valAPublicKey, + } + expectedValidators = append(expectedValidators, expectedValAConsumerValidator) + + // create a staking validator B that has set a consumer public key + valB := createStakingValidator(ctx, mocks, 2, 2) + // validator B has set a consumer key, the `ConsumerPublicKey` we expect is the key set by `SetValidatorConsumerPubKey` + valBConsumerKey := cryptotestutil.NewCryptoIdentityFromIntSeed(1).TMProtoCryptoPublicKey() + valBConsAddr, _ := valB.GetConsAddr() + providerKeeper.SetValidatorConsumerPubKey(ctx, chainID, types.NewProviderConsAddress(valBConsAddr), valBConsumerKey) + expectedValBConsumerValidator := types.ConsumerValidator{ + ProviderConsAddr: types.NewProviderConsAddress(valBConsAddr).Address.Bytes(), + Power: 2, + ConsumerPublicKey: &valBConsumerKey, + } + expectedValidators = append(expectedValidators, expectedValBConsumerValidator) + + // opt in validators A and B with 0 power and no consumer public keys + providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(valAConsAddr)) + providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(valBConsAddr)) + + // the expected actual validators are the opted-in validators but with the correct power and consumer public keys set + bondedValidators := []stakingtypes.Validator{valA, valB} + actualValidators := providerKeeper.FilterValidators(ctx, "chainID", bondedValidators, + func(providerAddr types.ProviderConsAddress) bool { + return providerKeeper.IsOptedIn(ctx, chainID, providerAddr) + }) + + // sort validators first to be able to compare + sortValidators := func(validators []types.ConsumerValidator) { + sort.Slice(validators, func(i, j int) bool { + return bytes.Compare(validators[i].ProviderConsAddr, validators[j].ProviderConsAddr) < 0 + }) + } + + sortValidators(actualValidators) + sortValidators(expectedValidators) + require.Equal(t, expectedValidators, actualValidators) + + // create a staking validator C that is not opted in, hence `expectedValidators` remains the same + valC := createStakingValidator(ctx, mocks, 3, 3) + bondedValidators = []stakingtypes.Validator{valA, valB, valC} + actualValidators = providerKeeper.FilterValidators(ctx, "chainID", bondedValidators, + func(providerAddr types.ProviderConsAddress) bool { + return providerKeeper.IsOptedIn(ctx, chainID, providerAddr) + }) + + sortValidators(actualValidators) + sortValidators(expectedValidators) + require.Equal(t, expectedValidators, actualValidators) +} + +func TestCreateConsumerValidator(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainID := "chainID" + + // create a validator which has set a consumer public key + valA := createStakingValidator(ctx, mocks, 0, 1) + valAConsumerKey := cryptotestutil.NewCryptoIdentityFromIntSeed(1).TMProtoCryptoPublicKey() + valAConsAddr, _ := valA.GetConsAddr() + valAProviderConsAddr := types.NewProviderConsAddress(valAConsAddr) + providerKeeper.SetValidatorConsumerPubKey(ctx, chainID, valAProviderConsAddr, valAConsumerKey) + actualConsumerValidatorA, err := providerKeeper.CreateConsumerValidator(ctx, chainID, valA) + expectedConsumerValidatorA := types.ConsumerValidator{ + ProviderConsAddr: valAProviderConsAddr.ToSdkConsAddr(), + Power: 1, + ConsumerPublicKey: &valAConsumerKey, + } + require.Equal(t, expectedConsumerValidatorA, actualConsumerValidatorA) + require.NoError(t, err) + + // create a validator which has not set a consumer public key + valB := createStakingValidator(ctx, mocks, 1, 2) + valBConsAddr, _ := valB.GetConsAddr() + valBProviderConsAddr := types.NewProviderConsAddress(valBConsAddr) + valBPublicKey, _ := valB.TmConsPublicKey() + actualConsumerValidatorB, err := providerKeeper.CreateConsumerValidator(ctx, chainID, valB) + expectedConsumerValidatorB := types.ConsumerValidator{ + ProviderConsAddr: valBProviderConsAddr.ToSdkConsAddr(), + Power: 2, + ConsumerPublicKey: &valBPublicKey, + } + require.Equal(t, expectedConsumerValidatorB, actualConsumerValidatorB) + require.NoError(t, err) +} diff --git a/x/ccv/provider/migrations/migrator.go b/x/ccv/provider/migrations/migrator.go index 7c52ee27ce..ea6e9e00d5 100644 --- a/x/ccv/provider/migrations/migrator.go +++ b/x/ccv/provider/migrations/migrator.go @@ -4,9 +4,10 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - v3 "github.com/cosmos/interchain-security/v4/x/ccv/provider/migrations/v3" - v4 "github.com/cosmos/interchain-security/v4/x/ccv/provider/migrations/v4" + providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + v3 "github.com/cosmos/interchain-security/v5/x/ccv/provider/migrations/v3" + v4 "github.com/cosmos/interchain-security/v5/x/ccv/provider/migrations/v4" + v5 "github.com/cosmos/interchain-security/v5/x/ccv/provider/migrations/v5" ) // Migrator is a struct for handling in-place store migrations. @@ -39,3 +40,10 @@ func (m Migrator) Migrate3to4(ctx sdktypes.Context) error { v4.MigrateParams(ctx, m.paramSpace) return nil } + +// MigrateXtoY migrates x/ccvprovider state from consensus version X to Y. +// The migration consists of setting a top N of 95 for all registered consumer chains. +func (m Migrator) MigrateXtoY(ctx sdktypes.Context) error { + v5.MigrateTopNForRegisteredChains(ctx, m.providerKeeper) + return nil +} diff --git a/x/ccv/provider/migrations/v3/migration_test.go b/x/ccv/provider/migrations/v3/migration_test.go index 630b8fd7dd..56d6b617d9 100644 --- a/x/ccv/provider/migrations/v3/migration_test.go +++ b/x/ccv/provider/migrations/v3/migration_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - testutil "github.com/cosmos/interchain-security/v4/testutil/keeper" + testutil "github.com/cosmos/interchain-security/v5/testutil/keeper" ) func TestMigrate2To3(t *testing.T) { diff --git a/x/ccv/provider/migrations/v3/migrations.go b/x/ccv/provider/migrations/v3/migrations.go index d308316761..2ffd1e6f25 100644 --- a/x/ccv/provider/migrations/v3/migrations.go +++ b/x/ccv/provider/migrations/v3/migrations.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" ) // MigrateQueuedPackets processes all queued packet data for all consumer chains that were stored diff --git a/x/ccv/provider/migrations/v4/migration_test.go b/x/ccv/provider/migrations/v4/migration_test.go index 5ab5faf87a..4423842149 100644 --- a/x/ccv/provider/migrations/v4/migration_test.go +++ b/x/ccv/provider/migrations/v4/migration_test.go @@ -5,8 +5,8 @@ import ( "github.com/stretchr/testify/require" - testutil "github.com/cosmos/interchain-security/v4/testutil/keeper" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + testutil "github.com/cosmos/interchain-security/v5/testutil/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) func TestMigrateParams(t *testing.T) { diff --git a/x/ccv/provider/migrations/v4/migrations.go b/x/ccv/provider/migrations/v4/migrations.go index 825d01e25d..e60c98700e 100644 --- a/x/ccv/provider/migrations/v4/migrations.go +++ b/x/ccv/provider/migrations/v4/migrations.go @@ -4,7 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) // MigrateParams adds missing provider chain params to the param store. diff --git a/x/ccv/provider/migrations/v5/migration_test.go b/x/ccv/provider/migrations/v5/migration_test.go new file mode 100644 index 0000000000..907aa1b019 --- /dev/null +++ b/x/ccv/provider/migrations/v5/migration_test.go @@ -0,0 +1,30 @@ +package v5 + +import ( + "testing" + + "github.com/stretchr/testify/require" + + testutil "github.com/cosmos/interchain-security/v5/testutil/keeper" +) + +func TestMigrateParams(t *testing.T) { + inMemParams := testutil.NewInMemKeeperParams(t) + provKeeper, ctx, ctrl, _ := testutil.GetProviderKeeperAndCtx(t, inMemParams) + defer ctrl.Finish() + + provKeeper.SetConsumerClientId(ctx, "chainID", "clientID") + + // initially top N should not exist + topN, found := provKeeper.GetTopN(ctx, "chainID") + require.False(t, found) + require.Zero(t, topN) + + // migrate + MigrateTopNForRegisteredChains(ctx, provKeeper) + + // after migration, top N should be 95 + topN, found = provKeeper.GetTopN(ctx, "chainID") + require.True(t, found) + require.Equal(t, uint32(95), topN) +} diff --git a/x/ccv/provider/migrations/v5/migrations.go b/x/ccv/provider/migrations/v5/migrations.go new file mode 100644 index 0000000000..aa228b6a09 --- /dev/null +++ b/x/ccv/provider/migrations/v5/migrations.go @@ -0,0 +1,24 @@ +package v5 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" +) + +// This migration only takes already registered chains into account. +// If a chain is in voting while the upgrade happens, this is not sufficient, +// and a migration to rewrite the proposal is needed. +func MigrateTopNForRegisteredChains(ctx sdk.Context, providerKeeper providerkeeper.Keeper) { + // get all consumer chains + registeredConsumerChains := providerKeeper.GetAllConsumerChains(ctx) + + // Set the topN of each chain to 95 + for _, chain := range registeredConsumerChains { + providerKeeper.SetTopN(ctx, chain.ChainId, 95) + } +} + +// // If there are consumer addition proposals in the voting period at the upgrade time, they may need the topN value updated. +// func MigrateTopNForVotingPeriodChains(ctx sdk.Context, govKeeper govkeeper.Keeper, providerKeeper providerkeeper.Keeper) { +// } diff --git a/x/ccv/provider/module.go b/x/ccv/provider/module.go index 4cdeac6af7..85555af2c2 100644 --- a/x/ccv/provider/module.go +++ b/x/ccv/provider/module.go @@ -19,10 +19,10 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/client/cli" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/migrations" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/client/cli" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/migrations" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) var ( @@ -137,7 +137,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw } // ConsensusVersion implements AppModule/ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 4 } +func (AppModule) ConsensusVersion() uint64 { return 5 } // BeginBlock implements the AppModule interface func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { @@ -147,6 +147,8 @@ func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { am.keeper.BeginBlockCCR(ctx) // Check for replenishing slash meter before any slash packets are processed for this block am.keeper.BeginBlockCIS(ctx) + // BeginBlock logic need for the Reward Distribution sub-protocol + am.keeper.BeginBlockRD(ctx, req) } // EndBlock implements the AppModule interface @@ -158,8 +160,6 @@ func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.V am.keeper.EndBlockCCR(ctx) // EndBlock logic needed for the Validator Set Update sub-protocol am.keeper.EndBlockVSU(ctx) - // EndBlock logic need for the Reward Distribution sub-protocol - am.keeper.EndBlockRD(ctx) return []abci.ValidatorUpdate{} } diff --git a/x/ccv/provider/module_test.go b/x/ccv/provider/module_test.go index 869c24253f..2dfdfc1598 100644 --- a/x/ccv/provider/module_test.go +++ b/x/ccv/provider/module_test.go @@ -11,10 +11,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/provider" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // Tests the provider's InitGenesis implementation against the spec. diff --git a/x/ccv/provider/proposal_handler.go b/x/ccv/provider/proposal_handler.go index 50089a8ab5..2a4342ea20 100644 --- a/x/ccv/provider/proposal_handler.go +++ b/x/ccv/provider/proposal_handler.go @@ -7,8 +7,8 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) // NewProviderProposalHandler defines the handler for consumer addition, diff --git a/x/ccv/provider/proposal_handler_test.go b/x/ccv/provider/proposal_handler_test.go index 65e84ac661..e15036f565 100644 --- a/x/ccv/provider/proposal_handler_test.go +++ b/x/ccv/provider/proposal_handler_test.go @@ -12,9 +12,9 @@ import ( distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" - "github.com/cosmos/interchain-security/v4/x/ccv/provider" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) // TestProviderProposalHandler tests the highest level handler for proposals @@ -44,6 +44,11 @@ func TestProviderProposalHandler(t *testing.T) { 100000000000, 100000000000, 100000000000, + 0, + 0, + 0, + nil, + nil, ), blockTime: hourFromNow, // ctx blocktime is after proposal's spawn time expValidConsumerAddition: true, diff --git a/x/ccv/provider/types/codec.go b/x/ccv/provider/types/codec.go index e5900bcf04..cc842276fb 100644 --- a/x/ccv/provider/types/codec.go +++ b/x/ccv/provider/types/codec.go @@ -46,10 +46,22 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { (*sdk.Msg)(nil), &MsgSubmitConsumerDoubleVoting{}, ) + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgOptIn{}, + ) + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgOptOut{}, + ) registry.RegisterImplementations( (*exported.ClientMessage)(nil), &tendermint.Misbehaviour{}, ) + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgSetConsumerCommissionRate{}, + ) msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) } diff --git a/x/ccv/provider/types/consumer.go b/x/ccv/provider/types/consumer.go index 4c43bd58e7..02651ac03e 100644 --- a/x/ccv/provider/types/consumer.go +++ b/x/ccv/provider/types/consumer.go @@ -1,7 +1,7 @@ package types import ( - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) func NewConsumerStates( diff --git a/x/ccv/provider/types/errors.go b/x/ccv/provider/types/errors.go index 315cda5197..32000ed65f 100644 --- a/x/ccv/provider/types/errors.go +++ b/x/ccv/provider/types/errors.go @@ -24,5 +24,7 @@ var ( ErrInvalidConsumerClient = errorsmod.Register(ModuleName, 16, "ccv channel is not built on correct client") ErrDuplicateConsumerChain = errorsmod.Register(ModuleName, 17, "consumer chain already exists") ErrConsumerChainNotFound = errorsmod.Register(ModuleName, 18, "consumer chain not found") - ErrNoUnconfirmedVSCPacket = errorsmod.Register(ModuleName, 19, "no unconfirmed vsc packet for this chain id") + ErrInvalidConsumerCommissionRate = errorsmod.Register(ModuleName, 19, "consumer commission rate is invalid") + ErrCannotOptOutFromTopN = errorsmod.Register(ModuleName, 20, "cannot opt out from a Top N chain") + ErrNoUnconfirmedVSCPacket = errorsmod.Register(ModuleName, 21, "no unconfirmed vsc packet for this chain id") ) diff --git a/x/ccv/provider/types/events.go b/x/ccv/provider/types/events.go index 58d686020f..8701821c52 100644 --- a/x/ccv/provider/types/events.go +++ b/x/ccv/provider/types/events.go @@ -7,6 +7,9 @@ const ( EventTypeAddConsumerRewardDenom = "add_consumer_reward_denom" EventTypeRemoveConsumerRewardDenom = "remove_consumer_reward_denom" EventTypeExecuteConsumerChainSlash = "execute_consumer_chain_slash" + EventTypeSetConsumerCommissionRate = "set_consumer_commission_rate" + EventTypeOptIn = "opt_in" + EventTypeOptOut = "opt_out" AttributeInfractionHeight = "infraction_height" AttributeInitialHeight = "initial_height" AttributeInitializationTimeout = "initialization_timeout" @@ -15,4 +18,6 @@ const ( AttributeProviderValidatorAddress = "provider_validator_address" AttributeConsumerConsensusPubKey = "consumer_consensus_pub_key" AttributeConsumerRewardDenom = "consumer_reward_denom" + AttributeConsumerCommissionRate = "consumer_commission_rate" + AttributeConsumerChainID = "consumer_chain_id" ) diff --git a/x/ccv/provider/types/genesis.go b/x/ccv/provider/types/genesis.go index ae929ba541..00802558c5 100644 --- a/x/ccv/provider/types/genesis.go +++ b/x/ccv/provider/types/genesis.go @@ -9,7 +9,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) func NewGenesisState( diff --git a/x/ccv/provider/types/genesis.pb.go b/x/ccv/provider/types/genesis.pb.go index d5041f1b10..d7d225b762 100644 --- a/x/ccv/provider/types/genesis.pb.go +++ b/x/ccv/provider/types/genesis.pb.go @@ -7,7 +7,7 @@ import ( fmt "fmt" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" - types "github.com/cosmos/interchain-security/v4/x/ccv/types" + types "github.com/cosmos/interchain-security/v5/x/ccv/types" io "io" math "math" math_bits "math/bits" diff --git a/x/ccv/provider/types/genesis_test.go b/x/ccv/provider/types/genesis_test.go index 41a716757f..d682e93897 100644 --- a/x/ccv/provider/types/genesis_test.go +++ b/x/ccv/provider/types/genesis_test.go @@ -14,9 +14,9 @@ import ( abci "github.com/cometbft/cometbft/abci/types" tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v4/testutil/crypto" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/testutil/crypto" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // Tests validation of consumer states and params within a provider genesis state diff --git a/x/ccv/provider/types/key_assignment.go b/x/ccv/provider/types/key_assignment.go index 04192ae53a..ee403e1e49 100644 --- a/x/ccv/provider/types/key_assignment.go +++ b/x/ccv/provider/types/key_assignment.go @@ -8,7 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // A validator's consensus address on the provider chain. diff --git a/x/ccv/provider/types/keys.go b/x/ccv/provider/types/keys.go index e9222ade98..4126a58cb7 100644 --- a/x/ccv/provider/types/keys.go +++ b/x/ccv/provider/types/keys.go @@ -8,7 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) type Status int @@ -147,9 +147,42 @@ const ( // ProposedConsumerChainByteKey is the byte prefix storing the consumer chainId in consumerAddition gov proposal submitted before voting finishes ProposedConsumerChainByteKey - // ConsumerValidatorBytePrefix is the byte prefix used when storing for each consumer chain all the consumer validators in this epoch + // ConsumerValidatorBytePrefix is the byte prefix used when storing for each consumer chain all the consumer + // validators in this epoch that are validating the consumer chain ConsumerValidatorBytePrefix + // OptedInBytePrefix is the byte prefix for storing whether a validator is opted in to validate on a consumer chain + OptedInBytePrefix + + // TopNBytePrefix is the byte prefix storing the mapping from a consumer chain to the N value of this chain, + // that corresponds to the N% of the top validators that have to validate this consumer chain + TopNBytePrefix + + // ValidatorsPowerCapPrefix is the byte prefix storing the mapping from a consumer chain to the power-cap value of this chain, + // that corresponds to p% such that no validator can have more than p% of the voting power on the consumer chain. + // Operates on a best-effort basis. + ValidatorsPowerCapPrefix + + // ValidatorSetCapPrefix is the byte prefix storing the mapping from a consumer chain to the validator-set cap value + // of this chain. + ValidatorSetCapPrefix + + // AllowlistPrefix is the byte prefix storing the mapping from a consumer chain to the set of validators that are + // allowlisted. + AllowlistPrefix + + // DenylistPrefix is the byte prefix storing the mapping from a consumer chain to the set of validators that are + // denylisted. + DenylistPrefix + + // ConsumerRewardsAllocationBytePrefix is the byte prefix for storing for each consumer the ICS rewards + // allocated to the consumer rewards pool + ConsumerRewardsAllocationBytePrefix + + // ConsumerCommissionRatePrefix is the byte prefix for storing the commission rate + // per validator per consumer chain + ConsumerCommissionRatePrefix + // NOTE: DO NOT ADD NEW BYTE PREFIXES HERE WITHOUT ADDING THEM TO getAllKeyPrefixes() IN keys_test.go ) @@ -522,6 +555,52 @@ func ConsumerValidatorKey(chainID string, providerAddr []byte) []byte { return append(prefix, providerAddr...) } +// TopNKey returns the key used to store the Top N value per consumer chain. +// This value corresponds to the N% of the top validators that have to validate the consumer chain. +func TopNKey(chainID string) []byte { + return ChainIdWithLenKey(TopNBytePrefix, chainID) +} + +// ValidatorSetPowerKey returns the key of consumer chain `chainID` +func ValidatorsPowerCapKey(chainID string) []byte { + return ChainIdWithLenKey(ValidatorsPowerCapPrefix, chainID) +} + +// ValidatorSetCapKey returns the key of consumer chain `chainID` +func ValidatorSetCapKey(chainID string) []byte { + return ChainIdWithLenKey(ValidatorSetCapPrefix, chainID) +} + +// AllowlistCapKey returns the key to a validator's slash log +func AllowlistCapKey(chainID string, providerAddr ProviderConsAddress) []byte { + return append(ChainIdWithLenKey(AllowlistPrefix, chainID), providerAddr.ToSdkConsAddr().Bytes()...) +} + +// DenylistCapKey returns the key to a validator's slash log +func DenylistCapKey(chainID string, providerAddr ProviderConsAddress) []byte { + return append(ChainIdWithLenKey(DenylistPrefix, chainID), providerAddr.ToSdkConsAddr().Bytes()...) +} + +// OptedInKey returns the key used to store whether a validator is opted in on a consumer chain. +func OptedInKey(chainID string, providerAddr ProviderConsAddress) []byte { + prefix := ChainIdWithLenKey(OptedInBytePrefix, chainID) + return append(prefix, providerAddr.ToSdkConsAddr().Bytes()...) +} + +// ConsumerRewardsAllocationKey returns the key used to store the ICS rewards per consumer chain +func ConsumerRewardsAllocationKey(chainID string) []byte { + return append([]byte{ConsumerRewardsAllocationBytePrefix}, []byte(chainID)...) +} + +// ConsumerCommissionRateKey returns the key used to store the commission rate per validator per consumer chain. +func ConsumerCommissionRateKey(chainID string, providerAddr ProviderConsAddress) []byte { + return ChainIdAndConsAddrKey( + ConsumerCommissionRatePrefix, + chainID, + providerAddr.ToSdkConsAddr(), + ) +} + // // End of generic helpers section // diff --git a/x/ccv/provider/types/keys_test.go b/x/ccv/provider/types/keys_test.go index 02faa9a640..78d8b4f806 100644 --- a/x/ccv/provider/types/keys_test.go +++ b/x/ccv/provider/types/keys_test.go @@ -8,8 +8,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - cryptoutil "github.com/cosmos/interchain-security/v4/testutil/crypto" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + cryptoutil "github.com/cosmos/interchain-security/v5/testutil/crypto" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) // Tests that all singular keys, or prefixes to fully resolves keys are non duplicate byte values. @@ -57,6 +57,10 @@ func getAllKeyPrefixes() []byte { providertypes.EquivocationEvidenceMinHeightBytePrefix, providertypes.ProposedConsumerChainByteKey, providertypes.ConsumerValidatorBytePrefix, + providertypes.OptedInBytePrefix, + providertypes.TopNBytePrefix, + providertypes.ConsumerRewardsAllocationBytePrefix, + providertypes.ConsumerCommissionRatePrefix, } } diff --git a/x/ccv/provider/types/msg.go b/x/ccv/provider/types/msg.go index 55e6b2fae4..b6546506fd 100644 --- a/x/ccv/provider/types/msg.go +++ b/x/ccv/provider/types/msg.go @@ -14,7 +14,7 @@ import ( tmtypes "github.com/cometbft/cometbft/proto/tendermint/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) // provider message types @@ -22,12 +22,18 @@ const ( TypeMsgAssignConsumerKey = "assign_consumer_key" TypeMsgSubmitConsumerMisbehaviour = "submit_consumer_misbehaviour" TypeMsgSubmitConsumerDoubleVoting = "submit_consumer_double_vote" + TypeMsgOptIn = "opt_in" + TypeMsgOptOut = "opt_out" + TypeMsgSetConsumerCommissionRate = "set_consumer_commission_rate" ) var ( _ sdk.Msg = &MsgAssignConsumerKey{} _ sdk.Msg = &MsgSubmitConsumerMisbehaviour{} _ sdk.Msg = &MsgSubmitConsumerDoubleVoting{} + _ sdk.Msg = &MsgOptIn{} + _ sdk.Msg = &MsgOptOut{} + _ sdk.Msg = &MsgSetConsumerCommissionRate{} ) // NewMsgAssignConsumerKey creates a new MsgAssignConsumerKey instance. @@ -203,3 +209,163 @@ func (msg MsgSubmitConsumerDoubleVoting) GetSigners() []sdk.AccAddress { } return []sdk.AccAddress{addr} } + +// NewMsgOptIn creates a new NewMsgOptIn instance. +func NewMsgOptIn(chainID string, providerValidatorAddress sdk.ValAddress, consumerConsensusPubKey string) (*MsgOptIn, error) { + return &MsgOptIn{ + ChainId: chainID, + ProviderAddr: providerValidatorAddress.String(), + ConsumerKey: consumerConsensusPubKey, + }, nil +} + +// Route implements the sdk.Msg interface. +func (msg MsgOptIn) Route() string { return RouterKey } + +// GetSigners implements the sdk.Msg interface. It returns the address(es) that +// must sign over msg.GetSignBytes(). +func (msg MsgOptIn) GetSigners() []sdk.AccAddress { + valAddr, err := sdk.ValAddressFromBech32(msg.ProviderAddr) + if err != nil { + // same behavior as in cosmos-sdk + panic(err) + } + return []sdk.AccAddress{valAddr.Bytes()} +} + +// GetSignBytes returns the message bytes to sign over. +func (msg MsgOptIn) GetSignBytes() []byte { + bz := ccvtypes.ModuleCdc.MustMarshalJSON(&msg) + return sdk.MustSortJSON(bz) +} + +// ValidateBasic implements the sdk.Msg interface. +func (msg MsgOptIn) ValidateBasic() error { + if strings.TrimSpace(msg.ChainId) == "" { + return errorsmod.Wrapf(ErrInvalidConsumerChainID, "chainId cannot be blank") + } + // It is possible to opt in to validate on consumer chains that are not yet approved. + // This can only be done by a signing validator, but it is still sensible + // to limit the chainID size to prevent abuse. + if 128 < len(msg.ChainId) { + return errorsmod.Wrapf(ErrInvalidConsumerChainID, "chainId cannot exceed 128 length") + } + _, err := sdk.ValAddressFromBech32(msg.ProviderAddr) + if err != nil { + return ErrInvalidProviderAddress + } + + if msg.ConsumerKey != "" { + if _, _, err := ParseConsumerKeyFromJson(msg.ConsumerKey); err != nil { + return ErrInvalidConsumerConsensusPubKey + } + } + return nil +} + +// NewMsgOptOut creates a new NewMsgOptIn instance. +func NewMsgOptOut(chainID string, providerValidatorAddress sdk.ValAddress) (*MsgOptOut, error) { + return &MsgOptOut{ + ChainId: chainID, + ProviderAddr: providerValidatorAddress.String(), + }, nil +} + +// Route implements the sdk.Msg interface. +func (msg MsgOptOut) Route() string { return RouterKey } + +// Type implements the sdk.Msg interface. +func (msg MsgOptIn) Type() string { + return TypeMsgOptIn +} + +// GetSigners implements the sdk.Msg interface. It returns the address(es) that +// must sign over msg.GetSignBytes(). +func (msg MsgOptOut) GetSigners() []sdk.AccAddress { + valAddr, err := sdk.ValAddressFromBech32(msg.ProviderAddr) + if err != nil { + // same behavior as in cosmos-sdk + panic(err) + } + return []sdk.AccAddress{valAddr.Bytes()} +} + +// GetSignBytes returns the message bytes to sign over. +func (msg MsgOptOut) GetSignBytes() []byte { + bz := ccvtypes.ModuleCdc.MustMarshalJSON(&msg) + return sdk.MustSortJSON(bz) +} + +// ValidateBasic implements the sdk.Msg interface. +func (msg MsgOptOut) ValidateBasic() error { + if strings.TrimSpace(msg.ChainId) == "" { + return errorsmod.Wrapf(ErrInvalidConsumerChainID, "chainId cannot be blank") + } + // It is possible to assign keys for consumer chains that are not yet approved. + // This can only be done by a signing validator, but it is still sensible + // to limit the chainID size to prevent abuse. + if 128 < len(msg.ChainId) { + return errorsmod.Wrapf(ErrInvalidConsumerChainID, "chainId cannot exceed 128 length") + } + _, err := sdk.ValAddressFromBech32(msg.ProviderAddr) + if err != nil { + return ErrInvalidProviderAddress + } + return nil +} + +// NewMsgSetConsumerCommissionRate creates a new MsgSetConsumerCommissionRate msg instance. +func NewMsgSetConsumerCommissionRate(chainID string, commission sdk.Dec, providerValidatorAddress sdk.ValAddress) *MsgSetConsumerCommissionRate { + return &MsgSetConsumerCommissionRate{ + ChainId: chainID, + Rate: commission, + ProviderAddr: providerValidatorAddress.String(), + } +} + +// Type implements the sdk.Msg interface. +func (msg MsgOptOut) Type() string { + return TypeMsgOptOut +} + +func (msg MsgSetConsumerCommissionRate) Route() string { + return RouterKey +} + +func (msg MsgSetConsumerCommissionRate) Type() string { + return TypeMsgSetConsumerCommissionRate +} + +func (msg MsgSetConsumerCommissionRate) ValidateBasic() error { + if strings.TrimSpace(msg.ChainId) == "" { + return errorsmod.Wrapf(ErrInvalidConsumerChainID, "chainId cannot be blank") + } + + if 128 < len(msg.ChainId) { + return errorsmod.Wrapf(ErrInvalidConsumerChainID, "chainId cannot exceed 128 length") + } + _, err := sdk.ValAddressFromBech32(msg.ProviderAddr) + if err != nil { + return ErrInvalidProviderAddress + } + + if msg.Rate.IsNegative() || msg.Rate.GT(sdk.OneDec()) { + return errorsmod.Wrapf(ErrInvalidConsumerCommissionRate, "consumer commission rate should be in the range [0, 1]") + } + + return nil +} + +func (msg MsgSetConsumerCommissionRate) GetSigners() []sdk.AccAddress { + valAddr, err := sdk.ValAddressFromBech32(msg.ProviderAddr) + if err != nil { + // same behavior as in cosmos-sdk + panic(err) + } + return []sdk.AccAddress{valAddr.Bytes()} +} + +func (msg MsgSetConsumerCommissionRate) GetSignBytes() []byte { + bz := ccvtypes.ModuleCdc.MustMarshalJSON(&msg) + return sdk.MustSortJSON(bz) +} diff --git a/x/ccv/provider/types/params.go b/x/ccv/provider/types/params.go index a0a7a5ed7a..c398188c98 100644 --- a/x/ccv/provider/types/params.go +++ b/x/ccv/provider/types/params.go @@ -11,7 +11,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) const ( diff --git a/x/ccv/provider/types/params_test.go b/x/ccv/provider/types/params_test.go index 4e72c233af..b86dd0cddf 100644 --- a/x/ccv/provider/types/params_test.go +++ b/x/ccv/provider/types/params_test.go @@ -11,7 +11,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) func TestValidateParams(t *testing.T) { diff --git a/x/ccv/provider/types/proposal.go b/x/ccv/provider/types/proposal.go index 93cadfd14e..d2cfa7ed3f 100644 --- a/x/ccv/provider/types/proposal.go +++ b/x/ccv/provider/types/proposal.go @@ -14,7 +14,7 @@ import ( evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" ) const ( @@ -49,6 +49,11 @@ func NewConsumerAdditionProposal(title, description, chainID string, ccvTimeoutPeriod time.Duration, transferTimeoutPeriod time.Duration, unbondingPeriod time.Duration, + topN uint32, + validatorsPowerCap uint32, + validatorSetCap uint32, + allowlist []string, + denylist []string, ) govv1beta1.Content { return &ConsumerAdditionProposal{ Title: title, @@ -65,6 +70,11 @@ func NewConsumerAdditionProposal(title, description, chainID string, CcvTimeoutPeriod: ccvTimeoutPeriod, TransferTimeoutPeriod: transferTimeoutPeriod, UnbondingPeriod: unbondingPeriod, + Top_N: topN, + ValidatorsPowerCap: validatorsPowerCap, + ValidatorSetCap: validatorSetCap, + Allowlist: allowlist, + Denylist: denylist, } } @@ -135,6 +145,16 @@ func (cccp *ConsumerAdditionProposal) ValidateBasic() error { return errorsmod.Wrap(ErrInvalidConsumerAdditionProposal, "unbonding period cannot be zero") } + // Top N corresponds to the top N% of validators that have to validate the consumer chain and can only be 0 (for an + // Opt In chain) or in the range [50, 100] (for a Top N chain). + if cccp.Top_N != 0 && (cccp.Top_N < 50 || cccp.Top_N > 100) { + return errorsmod.Wrap(ErrInvalidConsumerAdditionProposal, "Top N can either be 0 or in the range [50, 100]") + } + + if cccp.ValidatorsPowerCap != 0 && cccp.ValidatorSetCap > 100 { + return errorsmod.Wrap(ErrInvalidConsumerAdditionProposal, "validators' power cap has to be in the range [1, 100]") + } + return nil } diff --git a/x/ccv/provider/types/proposal_test.go b/x/ccv/provider/types/proposal_test.go index 72fcfe8436..a2f8f574ee 100644 --- a/x/ccv/provider/types/proposal_test.go +++ b/x/ccv/provider/types/proposal_test.go @@ -15,7 +15,7 @@ import ( govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) func TestConsumerAdditionProposalValidateBasic(t *testing.T) { @@ -36,6 +36,11 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 100000000000, 100000000000, 100000000000, + 0, + 0, + 0, + nil, + nil, ), true, }, @@ -48,7 +53,13 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 10000, 100000000000, 100000000000, - 100000000000), + 100000000000, + 0, + 0, + 0, + nil, + nil, + ), true, }, { @@ -60,7 +71,13 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 10000, 100000000000, 100000000000, - 100000000000), + 100000000000, + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -72,7 +89,13 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 10000, 100000000000, 100000000000, - 100000000000), + 100000000000, + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -104,7 +127,13 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 10000, 100000000000, 100000000000, - 100000000000), + 100000000000, + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -116,7 +145,12 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 10000, 100000000000, 100000000000, - 100000000000), + 100000000000, + 0, + 0, + 0, + nil, + nil), false, }, { @@ -128,7 +162,13 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 10000, 100000000000, 100000000000, - 100000000000), + 100000000000, + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -140,7 +180,13 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 10000, 100000000000, 100000000000, - 100000000000), + 100000000000, + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -152,7 +198,13 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 100000000000, 10000, 100000000000, - 100000000000), + 100000000000, + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -164,7 +216,13 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 10000, 100000000000, 100000000000, - 100000000000), + 100000000000, + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -176,7 +234,13 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { -2, 100000000000, 100000000000, - 100000000000), + 100000000000, + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -188,7 +252,13 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 10000, 0, 100000000000, - 100000000000), + 100000000000, + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -200,7 +270,13 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 10000, 100000000000, 0, - 100000000000), + 100000000000, + 0, + 0, + 0, + nil, + nil, + ), false, }, { @@ -212,7 +288,13 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 10000, 100000000000, 100000000000, - 0), + 0, + 0, + 0, + 0, + nil, + nil, + ), false, }, } @@ -236,7 +318,12 @@ func TestMarshalConsumerAdditionProposal(t *testing.T) { 10000, 100000000000, 100000000000, - 100000000000) + 100000000000, + 0, + 0, + 0, + nil, + nil) cccp, ok := content.(*types.ConsumerAdditionProposal) require.True(t, ok) @@ -278,7 +365,12 @@ func TestConsumerAdditionProposalString(t *testing.T) { 500000, 100000000000, 10000000000, - 100000000000) + 100000000000, + 0, + 0, + 0, + []string{}, + []string{}) expect := fmt.Sprintf(`CreateConsumerChain Proposal Title: title diff --git a/x/ccv/provider/types/provider.pb.go b/x/ccv/provider/types/provider.pb.go index 8a83879f7f..07e511669c 100644 --- a/x/ccv/provider/types/provider.pb.go +++ b/x/ccv/provider/types/provider.pb.go @@ -6,14 +6,16 @@ package types import ( fmt "fmt" crypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" types2 "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" types1 "github.com/cosmos/cosmos-sdk/x/evidence/types" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" types "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" _07_tendermint "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" - types3 "github.com/cosmos/interchain-security/v4/x/ccv/types" + types3 "github.com/cosmos/interchain-security/v5/x/ccv/types" _ "google.golang.org/protobuf/types/known/durationpb" _ "google.golang.org/protobuf/types/known/timestamppb" io "io" @@ -90,6 +92,25 @@ type ConsumerAdditionProposal struct { // chain. it is most relevant for chains performing a sovereign to consumer // changeover in order to maintain the existing ibc transfer channel DistributionTransmissionChannel string `protobuf:"bytes,14,opt,name=distribution_transmission_channel,json=distributionTransmissionChannel,proto3" json:"distribution_transmission_channel,omitempty"` + // Corresponds to the percentage of validators that have to validate the chain under the Top N case. + // For example, 53 corresponds to a Top 53% chain, meaning that the top 53% provider validators by voting power + // have to validate the proposed consumer chain. top_N can either be 0 or any value in [50, 100]. + // A chain can join with top_N == 0 as an Opt In chain, or with top_N ∈ [50, 100] as a Top N chain. + Top_N uint32 `protobuf:"varint,15,opt,name=top_N,json=topN,proto3" json:"top_N,omitempty"` + // Corresponds to the maximum power (percentage-wise) a validator can have on the consumer chain. For instance, if + // `validators_power_cap` is set to 32, it means that no validator can have more than 32% of the voting power on the + // consumer chain. Note that this might not be feasible. For example, think of a consumer chain with only + // 5 validators and with `validators_power_cap` set to 10%. In such a scenario, at least one validator would need + // to have more than 20% of the total voting power. Therefore, `validators_power_cap` operates on a best-effort basis. + ValidatorsPowerCap uint32 `protobuf:"varint,16,opt,name=validators_power_cap,json=validatorsPowerCap,proto3" json:"validators_power_cap,omitempty"` + // Corresponds to the maximum number of validators that can validate a consumer chain. + // Only applicable to Opt In chains. Setting `validator_set_cap` on a Top N chain is a no-op. + ValidatorSetCap uint32 `protobuf:"varint,17,opt,name=validator_set_cap,json=validatorSetCap,proto3" json:"validator_set_cap,omitempty"` + // Corresponds to a list of provider consensus addresses of validators that are the ONLY ones that can validate + // the consumer chain. + Allowlist []string `protobuf:"bytes,18,rep,name=allowlist,proto3" json:"allowlist,omitempty"` + // Corresponds to a list of provider consensus addresses of validators that CANNOT validate the consumer chain. + Denylist []string `protobuf:"bytes,19,rep,name=denylist,proto3" json:"denylist,omitempty"` } func (m *ConsumerAdditionProposal) Reset() { *m = ConsumerAdditionProposal{} } @@ -1459,6 +1480,53 @@ func (m *ConsumerValidator) GetConsumerPublicKey() *crypto.PublicKey { return nil } +// ConsumerRewardsAllocation stores the rewards allocated by a consumer chain +// to the consumer rewards pool. It is used to allocate the tokens to the consumer +// opted-in validators and the community pool during BeginBlock. +type ConsumerRewardsAllocation struct { + Rewards github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,1,rep,name=rewards,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"rewards"` +} + +func (m *ConsumerRewardsAllocation) Reset() { *m = ConsumerRewardsAllocation{} } +func (m *ConsumerRewardsAllocation) String() string { return proto.CompactTextString(m) } +func (*ConsumerRewardsAllocation) ProtoMessage() {} +func (*ConsumerRewardsAllocation) Descriptor() ([]byte, []int) { + return fileDescriptor_f22ec409a72b7b72, []int{23} +} +func (m *ConsumerRewardsAllocation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConsumerRewardsAllocation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConsumerRewardsAllocation.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ConsumerRewardsAllocation) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConsumerRewardsAllocation.Merge(m, src) +} +func (m *ConsumerRewardsAllocation) XXX_Size() int { + return m.Size() +} +func (m *ConsumerRewardsAllocation) XXX_DiscardUnknown() { + xxx_messageInfo_ConsumerRewardsAllocation.DiscardUnknown(m) +} + +var xxx_messageInfo_ConsumerRewardsAllocation proto.InternalMessageInfo + +func (m *ConsumerRewardsAllocation) GetRewards() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.Rewards + } + return nil +} + func init() { proto.RegisterType((*ConsumerAdditionProposal)(nil), "interchain_security.ccv.provider.v1.ConsumerAdditionProposal") proto.RegisterType((*ConsumerRemovalProposal)(nil), "interchain_security.ccv.provider.v1.ConsumerRemovalProposal") @@ -1483,6 +1551,7 @@ func init() { proto.RegisterType((*ValidatorByConsumerAddr)(nil), "interchain_security.ccv.provider.v1.ValidatorByConsumerAddr") proto.RegisterType((*ConsumerAddrsToPrune)(nil), "interchain_security.ccv.provider.v1.ConsumerAddrsToPrune") proto.RegisterType((*ConsumerValidator)(nil), "interchain_security.ccv.provider.v1.ConsumerValidator") + proto.RegisterType((*ConsumerRewardsAllocation)(nil), "interchain_security.ccv.provider.v1.ConsumerRewardsAllocation") } func init() { @@ -1490,117 +1559,127 @@ func init() { } var fileDescriptor_f22ec409a72b7b72 = []byte{ - // 1755 bytes of a gzipped FileDescriptorProto + // 1919 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x4f, 0x6f, 0x1b, 0xc7, - 0x15, 0xd7, 0x92, 0x94, 0x2c, 0x3e, 0x4a, 0x94, 0xb4, 0x52, 0xe2, 0x95, 0xab, 0x52, 0xf2, 0xa6, - 0x49, 0x55, 0xa4, 0x59, 0x56, 0x4a, 0x0b, 0x04, 0x46, 0x83, 0x40, 0xa2, 0x9c, 0x58, 0x56, 0x12, - 0x2b, 0x2b, 0x55, 0x46, 0xdb, 0xc3, 0x62, 0x38, 0x3b, 0x26, 0x07, 0x5a, 0xee, 0xac, 0x67, 0x66, - 0xd7, 0xe1, 0xa5, 0xe7, 0x1e, 0xd3, 0x5b, 0xd0, 0x4b, 0xd3, 0x02, 0x3d, 0xf7, 0x6b, 0xe4, 0x98, - 0x63, 0x4f, 0x49, 0x61, 0x1f, 0xfb, 0x25, 0x8a, 0x99, 0xfd, 0x4b, 0x4a, 0x72, 0x69, 0xb8, 0xbd, - 0xcd, 0xbe, 0x79, 0xef, 0xf7, 0xfe, 0xbf, 0x37, 0x24, 0xec, 0xd3, 0x50, 0x12, 0x8e, 0x87, 0x88, - 0x86, 0x9e, 0x20, 0x38, 0xe6, 0x54, 0x8e, 0xbb, 0x18, 0x27, 0xdd, 0x88, 0xb3, 0x84, 0xfa, 0x84, - 0x77, 0x93, 0xbd, 0xe2, 0xec, 0x44, 0x9c, 0x49, 0x66, 0xbe, 0x75, 0x8d, 0x8c, 0x83, 0x71, 0xe2, - 0x14, 0x7c, 0xc9, 0xde, 0x9d, 0xb7, 0x6f, 0x02, 0x4e, 0xf6, 0xba, 0xcf, 0x28, 0x27, 0x29, 0xd6, - 0x9d, 0x8d, 0x01, 0x1b, 0x30, 0x7d, 0xec, 0xaa, 0x53, 0x46, 0xdd, 0x1e, 0x30, 0x36, 0x08, 0x48, - 0x57, 0x7f, 0xf5, 0xe3, 0x27, 0x5d, 0x49, 0x47, 0x44, 0x48, 0x34, 0x8a, 0x32, 0x86, 0xce, 0x34, - 0x83, 0x1f, 0x73, 0x24, 0x29, 0x0b, 0x73, 0x00, 0xda, 0xc7, 0x5d, 0xcc, 0x38, 0xe9, 0xe2, 0x80, - 0x92, 0x50, 0x2a, 0xad, 0xe9, 0x29, 0x63, 0xe8, 0x2a, 0x86, 0x80, 0x0e, 0x86, 0x32, 0x25, 0x8b, - 0xae, 0x24, 0xa1, 0x4f, 0xf8, 0x88, 0xa6, 0xcc, 0xe5, 0x57, 0x26, 0xb0, 0x55, 0xb9, 0xc7, 0x7c, - 0x1c, 0x49, 0xd6, 0xbd, 0x24, 0x63, 0x91, 0xdd, 0xbe, 0x83, 0x99, 0x18, 0x31, 0xd1, 0x25, 0xca, - 0xff, 0x10, 0x93, 0x6e, 0xb2, 0xd7, 0x27, 0x12, 0xed, 0x15, 0x84, 0xdc, 0xee, 0x8c, 0xaf, 0x8f, - 0x44, 0xc9, 0x83, 0x19, 0xcd, 0xec, 0xb6, 0x7f, 0x58, 0x00, 0xab, 0xc7, 0x42, 0x11, 0x8f, 0x08, - 0x3f, 0xf0, 0x7d, 0xaa, 0x5c, 0x3a, 0xe5, 0x2c, 0x62, 0x02, 0x05, 0xe6, 0x06, 0xcc, 0x4b, 0x2a, - 0x03, 0x62, 0x19, 0x3b, 0xc6, 0x6e, 0xd3, 0x4d, 0x3f, 0xcc, 0x1d, 0x68, 0xf9, 0x44, 0x60, 0x4e, - 0x23, 0xc5, 0x6c, 0xd5, 0xf4, 0x5d, 0x95, 0x64, 0x6e, 0xc2, 0x62, 0x9a, 0x07, 0xea, 0x5b, 0x75, - 0x7d, 0x7d, 0x4b, 0x7f, 0x1f, 0xfb, 0xe6, 0x27, 0xd0, 0xa6, 0x21, 0x95, 0x14, 0x05, 0xde, 0x90, - 0xa8, 0x68, 0x58, 0x8d, 0x1d, 0x63, 0xb7, 0xb5, 0x7f, 0xc7, 0xa1, 0x7d, 0xec, 0xa8, 0x00, 0x3a, - 0x59, 0xd8, 0x92, 0x3d, 0xe7, 0x81, 0xe6, 0x38, 0x6c, 0x7c, 0xfb, 0xfd, 0xf6, 0x9c, 0xbb, 0x9c, - 0xc9, 0xa5, 0x44, 0xf3, 0x2e, 0x2c, 0x0d, 0x48, 0x48, 0x04, 0x15, 0xde, 0x10, 0x89, 0xa1, 0x35, - 0xbf, 0x63, 0xec, 0x2e, 0xb9, 0xad, 0x8c, 0xf6, 0x00, 0x89, 0xa1, 0xb9, 0x0d, 0xad, 0x3e, 0x0d, - 0x11, 0x1f, 0xa7, 0x1c, 0x0b, 0x9a, 0x03, 0x52, 0x92, 0x66, 0xe8, 0x01, 0x88, 0x08, 0x3d, 0x0b, - 0x3d, 0x95, 0x6d, 0xeb, 0x56, 0x66, 0x48, 0x9a, 0x69, 0x27, 0xcf, 0xb4, 0x73, 0x9e, 0x97, 0xc2, - 0xe1, 0xa2, 0x32, 0xe4, 0xab, 0x1f, 0xb6, 0x0d, 0xb7, 0xa9, 0xe5, 0xd4, 0x8d, 0xf9, 0x39, 0xac, - 0xc6, 0x61, 0x9f, 0x85, 0x3e, 0x0d, 0x07, 0x5e, 0x44, 0x38, 0x65, 0xbe, 0xb5, 0xa8, 0xa1, 0x36, - 0xaf, 0x40, 0x1d, 0x65, 0x45, 0x93, 0x22, 0x7d, 0xad, 0x90, 0x56, 0x0a, 0xe1, 0x53, 0x2d, 0x6b, - 0x7e, 0x01, 0x26, 0xc6, 0x89, 0x36, 0x89, 0xc5, 0x32, 0x47, 0x6c, 0xce, 0x8e, 0xb8, 0x8a, 0x71, - 0x72, 0x9e, 0x4a, 0x67, 0x90, 0xbf, 0x87, 0xdb, 0x92, 0xa3, 0x50, 0x3c, 0x21, 0x7c, 0x1a, 0x17, - 0x66, 0xc7, 0x7d, 0x23, 0xc7, 0x98, 0x04, 0x7f, 0x00, 0x3b, 0x38, 0x2b, 0x20, 0x8f, 0x13, 0x9f, - 0x0a, 0xc9, 0x69, 0x3f, 0x56, 0xb2, 0xde, 0x13, 0x8e, 0xb0, 0xae, 0x91, 0x96, 0x2e, 0x82, 0x4e, - 0xce, 0xe7, 0x4e, 0xb0, 0x7d, 0x9c, 0x71, 0x99, 0x8f, 0xe0, 0x27, 0xfd, 0x80, 0xe1, 0x4b, 0xa1, - 0x8c, 0xf3, 0x26, 0x90, 0xb4, 0xea, 0x11, 0x15, 0x42, 0xa1, 0x2d, 0xed, 0x18, 0xbb, 0x75, 0xf7, - 0x6e, 0xca, 0x7b, 0x4a, 0xf8, 0x51, 0x85, 0xf3, 0xbc, 0xc2, 0x68, 0xbe, 0x07, 0xe6, 0x90, 0x0a, - 0xc9, 0x38, 0xc5, 0x28, 0xf0, 0x48, 0x28, 0x39, 0x25, 0xc2, 0x5a, 0xd6, 0xe2, 0x6b, 0xe5, 0xcd, - 0xfd, 0xf4, 0xc2, 0x7c, 0x08, 0x77, 0x6f, 0x54, 0xea, 0xe1, 0x21, 0x0a, 0x43, 0x12, 0x58, 0x6d, - 0xed, 0xca, 0xb6, 0x7f, 0x83, 0xce, 0x5e, 0xca, 0x76, 0x6f, 0xf1, 0x8f, 0xdf, 0x6c, 0xcf, 0x7d, - 0xfd, 0xcd, 0xf6, 0x9c, 0xfd, 0x0f, 0x03, 0x6e, 0xf7, 0x0a, 0xc7, 0x47, 0x2c, 0x41, 0xc1, 0xff, - 0xb3, 0xc1, 0x0e, 0xa0, 0x29, 0x24, 0x8b, 0xd2, 0x92, 0x6e, 0xbc, 0x42, 0x49, 0x2f, 0x2a, 0x31, - 0x75, 0x61, 0xff, 0xc5, 0x80, 0x8d, 0xfb, 0x4f, 0x63, 0x9a, 0x30, 0x8c, 0xfe, 0x27, 0xf3, 0xe0, - 0x04, 0x96, 0x49, 0x05, 0x4f, 0x58, 0xf5, 0x9d, 0xfa, 0x6e, 0x6b, 0xff, 0x6d, 0x27, 0x1d, 0x4e, - 0x4e, 0x31, 0xb3, 0xb2, 0x01, 0xe5, 0x54, 0xb5, 0xbb, 0x93, 0xb2, 0xf7, 0x6a, 0x96, 0x61, 0xff, - 0xcd, 0x80, 0x3b, 0x2a, 0xd2, 0x03, 0xe2, 0x92, 0x67, 0x88, 0xfb, 0x47, 0x24, 0x64, 0x23, 0xf1, - 0xda, 0x76, 0xda, 0xb0, 0xec, 0x6b, 0x24, 0x4f, 0x32, 0x0f, 0xf9, 0xbe, 0xb6, 0x53, 0xf3, 0x28, - 0xe2, 0x39, 0x3b, 0xf0, 0x7d, 0x73, 0x17, 0x56, 0x4b, 0x1e, 0xae, 0xf2, 0xa9, 0xc2, 0xac, 0xd8, - 0xda, 0x39, 0x9b, 0xce, 0x32, 0xb1, 0xff, 0x6d, 0xc0, 0xea, 0x27, 0x01, 0xeb, 0xa3, 0xe0, 0x2c, - 0x40, 0x62, 0xa8, 0xaa, 0x6c, 0xac, 0xd2, 0xc3, 0x49, 0xd6, 0xde, 0xda, 0xbc, 0x99, 0xd3, 0xa3, - 0xc4, 0xf4, 0xc0, 0xf9, 0x08, 0xd6, 0x8a, 0x86, 0x2b, 0xaa, 0x40, 0x7b, 0x73, 0xb8, 0xfe, 0xfc, - 0xfb, 0xed, 0x95, 0xbc, 0xd8, 0x7a, 0xba, 0x22, 0x8e, 0xdc, 0x15, 0x3c, 0x41, 0xf0, 0xcd, 0x0e, - 0xb4, 0x68, 0x1f, 0x7b, 0x82, 0x3c, 0xf5, 0xc2, 0x78, 0xa4, 0x0b, 0xa8, 0xe1, 0x36, 0x69, 0x1f, - 0x9f, 0x91, 0xa7, 0x9f, 0xc7, 0x23, 0xf3, 0x7d, 0x78, 0x33, 0x5f, 0xac, 0x5e, 0x82, 0x02, 0x4f, - 0xc9, 0xab, 0x70, 0x70, 0x5d, 0x4f, 0x4b, 0xee, 0x7a, 0x7e, 0x7b, 0x81, 0x02, 0xa5, 0xec, 0xc0, - 0xf7, 0xb9, 0xfd, 0x62, 0x1e, 0x16, 0x4e, 0x11, 0x47, 0x23, 0x61, 0x9e, 0xc3, 0x8a, 0x24, 0xa3, - 0x28, 0x40, 0x92, 0x78, 0xe9, 0x30, 0xcf, 0x3c, 0x7d, 0x57, 0x0f, 0xf9, 0xea, 0x12, 0x74, 0x2a, - 0x6b, 0x2f, 0xd9, 0x73, 0x7a, 0x9a, 0x7a, 0x26, 0x91, 0x24, 0x6e, 0x3b, 0xc7, 0x48, 0x89, 0xe6, - 0x07, 0x60, 0x49, 0x1e, 0x0b, 0x59, 0x8e, 0xd9, 0x72, 0xbe, 0xa4, 0xb9, 0x7c, 0x33, 0xbf, 0x4f, - 0x27, 0x53, 0x31, 0x57, 0xae, 0x9f, 0xa8, 0xf5, 0xd7, 0x99, 0xa8, 0x67, 0xb0, 0xae, 0xd6, 0xd1, - 0x34, 0x66, 0x63, 0x76, 0xcc, 0x35, 0x25, 0x3f, 0x09, 0xfa, 0x05, 0x98, 0x89, 0xc0, 0xd3, 0x98, - 0xf3, 0xaf, 0x60, 0x67, 0x22, 0xf0, 0x24, 0xa4, 0x0f, 0x5b, 0x42, 0x15, 0x9f, 0x37, 0x22, 0x52, - 0xcf, 0xe7, 0x28, 0x20, 0x21, 0x15, 0xc3, 0x1c, 0x7c, 0x61, 0x76, 0xf0, 0x4d, 0x0d, 0xf4, 0x99, - 0xc2, 0x71, 0x73, 0x98, 0x4c, 0x4b, 0x0f, 0x3a, 0xd7, 0x6b, 0x29, 0x12, 0x74, 0x4b, 0x27, 0xe8, - 0x47, 0xd7, 0x40, 0x14, 0x59, 0x12, 0xf0, 0x4e, 0x65, 0x8f, 0xa8, 0xae, 0xf6, 0x74, 0x43, 0x79, - 0x9c, 0x0c, 0xd4, 0xb0, 0x45, 0xe9, 0x4a, 0x21, 0xa4, 0xd8, 0x85, 0xd9, 0xf4, 0x50, 0x4f, 0x9b, - 0x62, 0x72, 0xf4, 0x18, 0x0d, 0xb3, 0x07, 0x83, 0x5d, 0xae, 0x9b, 0x62, 0x46, 0xb8, 0x15, 0xac, - 0x8f, 0x09, 0x51, 0xdd, 0x5c, 0x59, 0x39, 0x24, 0x62, 0x78, 0xa8, 0x57, 0x62, 0xdd, 0x6d, 0x17, - 0xeb, 0xe5, 0xbe, 0xa2, 0x3e, 0x6c, 0x2c, 0x2e, 0xae, 0x36, 0xed, 0x9f, 0x41, 0x53, 0x37, 0xf3, - 0x01, 0xbe, 0x14, 0xe6, 0x16, 0x34, 0x55, 0x57, 0x10, 0x21, 0x88, 0xb0, 0x0c, 0x3d, 0x03, 0x4a, - 0x82, 0x2d, 0x61, 0xf3, 0xa6, 0x87, 0x95, 0x30, 0x1f, 0xc3, 0xad, 0x88, 0xe8, 0xad, 0xaf, 0x05, - 0x5b, 0xfb, 0x1f, 0x3a, 0x33, 0xbc, 0x71, 0x9d, 0x9b, 0x00, 0xdd, 0x1c, 0xcd, 0xe6, 0xe5, 0x73, - 0x6e, 0x6a, 0xd9, 0x08, 0xf3, 0x62, 0x5a, 0xe9, 0xaf, 0x5f, 0x49, 0xe9, 0x14, 0x5e, 0xa9, 0xf3, - 0x5d, 0x68, 0x1d, 0xa4, 0x6e, 0x7f, 0x4a, 0x85, 0xbc, 0x1a, 0x96, 0xa5, 0x6a, 0x58, 0x1e, 0x42, - 0x3b, 0xdb, 0x91, 0xe7, 0x4c, 0x0f, 0x24, 0xf3, 0xc7, 0x00, 0xd9, 0x72, 0x55, 0x83, 0x2c, 0x1d, - 0xd9, 0xcd, 0x8c, 0x72, 0xec, 0x4f, 0xec, 0xba, 0xda, 0xc4, 0xae, 0xb3, 0x5d, 0x58, 0xb9, 0x10, - 0xf8, 0x37, 0xf9, 0x03, 0xea, 0x51, 0x24, 0xcc, 0x37, 0x60, 0x41, 0xf5, 0x50, 0x06, 0xd4, 0x70, - 0xe7, 0x13, 0x81, 0x8f, 0xf5, 0xd4, 0x2e, 0x1f, 0x69, 0x2c, 0xf2, 0xa8, 0x2f, 0xac, 0xda, 0x4e, - 0x7d, 0xb7, 0xe1, 0xb6, 0xe3, 0x52, 0xfc, 0xd8, 0x17, 0xf6, 0x6f, 0xa1, 0x55, 0x01, 0x34, 0xdb, - 0x50, 0x2b, 0xb0, 0x6a, 0xd4, 0x37, 0xef, 0xc1, 0x66, 0x09, 0x34, 0x39, 0x86, 0x53, 0xc4, 0xa6, - 0x7b, 0xbb, 0x60, 0x98, 0x98, 0xc4, 0xc2, 0x7e, 0x04, 0x1b, 0xc7, 0x65, 0xd3, 0x17, 0x43, 0x7e, - 0xc2, 0x43, 0x63, 0x72, 0x9b, 0x6f, 0x41, 0xb3, 0xf8, 0x25, 0xa2, 0xbd, 0x6f, 0xb8, 0x25, 0xc1, - 0x1e, 0xc1, 0xea, 0x85, 0xc0, 0x67, 0x24, 0xf4, 0x4b, 0xb0, 0x1b, 0x02, 0x70, 0x38, 0x0d, 0x34, - 0xf3, 0x4b, 0xb7, 0x54, 0xc7, 0x60, 0xf3, 0x02, 0x05, 0xd4, 0x47, 0x92, 0xf1, 0x33, 0x22, 0xd3, - 0x05, 0x7c, 0x8a, 0xf0, 0x25, 0x91, 0xc2, 0x74, 0xa1, 0x11, 0x50, 0x21, 0xb3, 0xca, 0xfa, 0xe0, - 0xc6, 0xca, 0x4a, 0xf6, 0x9c, 0x9b, 0x40, 0x8e, 0x90, 0x44, 0x59, 0xef, 0x6a, 0x2c, 0xfb, 0xa7, - 0xb0, 0xfe, 0x19, 0x92, 0x31, 0x27, 0xfe, 0x44, 0x8e, 0x57, 0xa1, 0xae, 0xf2, 0x67, 0xe8, 0xfc, - 0xa9, 0xa3, 0x7a, 0x0f, 0x58, 0xf7, 0xbf, 0x8c, 0x18, 0x97, 0xc4, 0xbf, 0x12, 0x91, 0x97, 0x84, - 0xf7, 0x12, 0xd6, 0x55, 0xb0, 0x04, 0x09, 0x7d, 0xaf, 0xf0, 0x33, 0xcd, 0x63, 0x6b, 0xff, 0x57, - 0x33, 0x75, 0xc7, 0xb4, 0xba, 0xcc, 0x81, 0xb5, 0x64, 0x8a, 0x2e, 0xec, 0x3f, 0x19, 0x60, 0x9d, - 0x90, 0xf1, 0x81, 0x10, 0x74, 0x10, 0x8e, 0x48, 0x28, 0xd5, 0x0c, 0x44, 0x98, 0xa8, 0xa3, 0xf9, - 0x16, 0x2c, 0x17, 0x3b, 0x57, 0xaf, 0x5a, 0x43, 0xaf, 0xda, 0xa5, 0x9c, 0xa8, 0x1a, 0xcc, 0xbc, - 0x07, 0x10, 0x71, 0x92, 0x78, 0xd8, 0xbb, 0x24, 0xe3, 0x2c, 0x8b, 0x5b, 0xd5, 0x15, 0x9a, 0xfe, - 0x4e, 0x74, 0x4e, 0xe3, 0x7e, 0x40, 0xf1, 0x09, 0x19, 0xbb, 0x8b, 0x8a, 0xbf, 0x77, 0x42, 0xc6, - 0xea, 0x4d, 0x14, 0xb1, 0x67, 0x84, 0xeb, 0xbd, 0x57, 0x77, 0xd3, 0x0f, 0xfb, 0xcf, 0x06, 0xdc, - 0x2e, 0xd2, 0x91, 0x97, 0xeb, 0x69, 0xdc, 0x57, 0x12, 0x2f, 0x89, 0xdb, 0x15, 0x6b, 0x6b, 0xd7, - 0x58, 0xfb, 0x11, 0x2c, 0x15, 0x0d, 0xa2, 0xec, 0xad, 0xcf, 0x60, 0x6f, 0x2b, 0x97, 0x38, 0x21, - 0x63, 0xfb, 0x0f, 0x15, 0xdb, 0x0e, 0xc7, 0x95, 0xd9, 0xc7, 0xff, 0x8b, 0x6d, 0x85, 0xda, 0xaa, - 0x6d, 0xb8, 0x2a, 0x7f, 0xc5, 0x81, 0xfa, 0x55, 0x07, 0xec, 0xbf, 0x1a, 0xb0, 0x51, 0xd5, 0x2a, - 0xce, 0xd9, 0x29, 0x8f, 0x43, 0xf2, 0x32, 0xed, 0x65, 0xfb, 0xd5, 0xaa, 0xed, 0xf7, 0x18, 0xda, - 0x13, 0x46, 0x89, 0x2c, 0x1a, 0xbf, 0x98, 0xa9, 0xc6, 0x2a, 0xd3, 0xd5, 0x5d, 0xae, 0xfa, 0x21, - 0xec, 0xbf, 0x1b, 0xb0, 0x96, 0xdb, 0x58, 0x04, 0xcb, 0xfc, 0x39, 0x98, 0x85, 0x7b, 0xe5, 0xeb, - 0x2d, 0x2d, 0xa9, 0xd5, 0xfc, 0x26, 0x7f, 0xba, 0x95, 0xa5, 0x51, 0xab, 0x94, 0x86, 0xf9, 0x29, - 0xac, 0x17, 0x26, 0x47, 0x3a, 0x41, 0x33, 0x67, 0xb1, 0x78, 0x9f, 0x16, 0xa4, 0xc3, 0xc7, 0xdf, - 0x3e, 0xef, 0x18, 0xdf, 0x3d, 0xef, 0x18, 0xff, 0x7a, 0xde, 0x31, 0xbe, 0x7a, 0xd1, 0x99, 0xfb, - 0xee, 0x45, 0x67, 0xee, 0x9f, 0x2f, 0x3a, 0x73, 0xbf, 0xfb, 0x70, 0x40, 0xe5, 0x30, 0xee, 0x3b, - 0x98, 0x8d, 0xba, 0xd9, 0x9f, 0x15, 0x65, 0x4c, 0xde, 0x2b, 0xfe, 0xc9, 0x49, 0x7e, 0xd9, 0xfd, - 0x72, 0xf2, 0x7f, 0x22, 0x39, 0x8e, 0x88, 0xe8, 0x2f, 0xe8, 0xe9, 0xf5, 0xfe, 0x7f, 0x02, 0x00, - 0x00, 0xff, 0xff, 0x0a, 0xef, 0x81, 0x2b, 0x58, 0x12, 0x00, 0x00, + 0x15, 0xd7, 0x8a, 0x94, 0x45, 0x0e, 0xf5, 0x77, 0xa4, 0xc4, 0x2b, 0x55, 0xa5, 0xe8, 0x4d, 0x93, + 0xaa, 0x71, 0xbd, 0x1b, 0x29, 0x2d, 0x60, 0x18, 0x0d, 0x02, 0x89, 0x72, 0x62, 0x59, 0x89, 0xcd, + 0xac, 0x54, 0x19, 0x6d, 0x0f, 0x8b, 0xe1, 0xec, 0x98, 0x1c, 0x68, 0xb9, 0xb3, 0x9e, 0x19, 0xae, + 0xc2, 0x4b, 0xcf, 0x3d, 0xb4, 0x40, 0x7a, 0x0b, 0x7a, 0x69, 0x5a, 0xa0, 0x40, 0xd1, 0x4b, 0xfb, + 0x31, 0x72, 0xcc, 0xb1, 0xa7, 0xa4, 0xb0, 0x0f, 0x3d, 0xf4, 0x4b, 0x14, 0x33, 0xfb, 0x97, 0x94, + 0xe4, 0xd2, 0x48, 0x73, 0x91, 0x76, 0xdf, 0xbc, 0xf7, 0x7b, 0x6f, 0xe6, 0xbd, 0x79, 0xbf, 0xc7, + 0x05, 0x7b, 0x34, 0x94, 0x84, 0xe3, 0x3e, 0xa2, 0xa1, 0x27, 0x08, 0x1e, 0x72, 0x2a, 0x47, 0x0e, + 0xc6, 0xb1, 0x13, 0x71, 0x16, 0x53, 0x9f, 0x70, 0x27, 0xde, 0xcd, 0x9f, 0xed, 0x88, 0x33, 0xc9, + 0xe0, 0x1b, 0x57, 0xd8, 0xd8, 0x18, 0xc7, 0x76, 0xae, 0x17, 0xef, 0x6e, 0xbe, 0x79, 0x1d, 0x70, + 0xbc, 0xeb, 0x5c, 0x50, 0x4e, 0x12, 0xac, 0xcd, 0xf5, 0x1e, 0xeb, 0x31, 0xfd, 0xe8, 0xa8, 0xa7, + 0x54, 0xba, 0xdd, 0x63, 0xac, 0x17, 0x10, 0x47, 0xbf, 0x75, 0x87, 0x4f, 0x1d, 0x49, 0x07, 0x44, + 0x48, 0x34, 0x88, 0x52, 0x85, 0xe6, 0xa4, 0x82, 0x3f, 0xe4, 0x48, 0x52, 0x16, 0x66, 0x00, 0xb4, + 0x8b, 0x1d, 0xcc, 0x38, 0x71, 0x70, 0x40, 0x49, 0x28, 0x95, 0xd7, 0xe4, 0x29, 0x55, 0x70, 0x94, + 0x42, 0x40, 0x7b, 0x7d, 0x99, 0x88, 0x85, 0x23, 0x49, 0xe8, 0x13, 0x3e, 0xa0, 0x89, 0x72, 0xf1, + 0x96, 0x1a, 0x6c, 0x95, 0xd6, 0x31, 0x1f, 0x45, 0x92, 0x39, 0xe7, 0x64, 0x24, 0xd2, 0xd5, 0xb7, + 0x30, 0x13, 0x03, 0x26, 0x1c, 0xa2, 0xf6, 0x1f, 0x62, 0xe2, 0xc4, 0xbb, 0x5d, 0x22, 0xd1, 0x6e, + 0x2e, 0xc8, 0xe2, 0x4e, 0xf5, 0xba, 0x48, 0x14, 0x3a, 0x98, 0xd1, 0x2c, 0xee, 0x55, 0x34, 0xa0, + 0x21, 0x73, 0xf4, 0xdf, 0x44, 0x64, 0xfd, 0xb6, 0x06, 0xcc, 0x36, 0x0b, 0xc5, 0x70, 0x40, 0xf8, + 0xbe, 0xef, 0x53, 0xb5, 0xcb, 0x0e, 0x67, 0x11, 0x13, 0x28, 0x80, 0xeb, 0x60, 0x4e, 0x52, 0x19, + 0x10, 0xd3, 0x68, 0x19, 0x3b, 0x75, 0x37, 0x79, 0x81, 0x2d, 0xd0, 0xf0, 0x89, 0xc0, 0x9c, 0x46, + 0x4a, 0xd9, 0x9c, 0xd5, 0x6b, 0x65, 0x11, 0xdc, 0x00, 0xb5, 0x24, 0x35, 0xd4, 0x37, 0x2b, 0x7a, + 0x79, 0x5e, 0xbf, 0x1f, 0xf9, 0xf0, 0x43, 0xb0, 0x44, 0x43, 0x2a, 0x29, 0x0a, 0xbc, 0x3e, 0x51, + 0x07, 0x64, 0x56, 0x5b, 0xc6, 0x4e, 0x63, 0x6f, 0xd3, 0xa6, 0x5d, 0x6c, 0xab, 0x33, 0xb5, 0xd3, + 0x93, 0x8c, 0x77, 0xed, 0x07, 0x5a, 0xe3, 0xa0, 0xfa, 0xe5, 0xd7, 0xdb, 0x33, 0xee, 0x62, 0x6a, + 0x97, 0x08, 0xe1, 0x2d, 0xb0, 0xd0, 0x23, 0x21, 0x11, 0x54, 0x78, 0x7d, 0x24, 0xfa, 0xe6, 0x5c, + 0xcb, 0xd8, 0x59, 0x70, 0x1b, 0xa9, 0xec, 0x01, 0x12, 0x7d, 0xb8, 0x0d, 0x1a, 0x5d, 0x1a, 0x22, + 0x3e, 0x4a, 0x34, 0x6e, 0x68, 0x0d, 0x90, 0x88, 0xb4, 0x42, 0x1b, 0x00, 0x11, 0xa1, 0x8b, 0xd0, + 0x53, 0x05, 0x60, 0xce, 0xa7, 0x81, 0x24, 0xc9, 0xb7, 0xb3, 0xe4, 0xdb, 0xa7, 0x59, 0x75, 0x1c, + 0xd4, 0x54, 0x20, 0x9f, 0x7d, 0xb3, 0x6d, 0xb8, 0x75, 0x6d, 0xa7, 0x56, 0xe0, 0x23, 0xb0, 0x32, + 0x0c, 0xbb, 0x2c, 0xf4, 0x69, 0xd8, 0xf3, 0x22, 0xc2, 0x29, 0xf3, 0xcd, 0x9a, 0x86, 0xda, 0xb8, + 0x04, 0x75, 0x98, 0xd6, 0x51, 0x82, 0xf4, 0xb9, 0x42, 0x5a, 0xce, 0x8d, 0x3b, 0xda, 0x16, 0x7e, + 0x02, 0x20, 0xc6, 0xb1, 0x0e, 0x89, 0x0d, 0x65, 0x86, 0x58, 0x9f, 0x1e, 0x71, 0x05, 0xe3, 0xf8, + 0x34, 0xb1, 0x4e, 0x21, 0x7f, 0x05, 0x6e, 0x4a, 0x8e, 0x42, 0xf1, 0x94, 0xf0, 0x49, 0x5c, 0x30, + 0x3d, 0xee, 0x6b, 0x19, 0xc6, 0x38, 0xf8, 0x03, 0xd0, 0xc2, 0x69, 0x01, 0x79, 0x9c, 0xf8, 0x54, + 0x48, 0x4e, 0xbb, 0x43, 0x65, 0xeb, 0x3d, 0xe5, 0x08, 0xeb, 0x1a, 0x69, 0xe8, 0x22, 0x68, 0x66, + 0x7a, 0xee, 0x98, 0xda, 0x07, 0xa9, 0x16, 0x7c, 0x0c, 0x7e, 0xd0, 0x0d, 0x18, 0x3e, 0x17, 0x2a, + 0x38, 0x6f, 0x0c, 0x49, 0xbb, 0x1e, 0x50, 0x21, 0x14, 0xda, 0x42, 0xcb, 0xd8, 0xa9, 0xb8, 0xb7, + 0x12, 0xdd, 0x0e, 0xe1, 0x87, 0x25, 0xcd, 0xd3, 0x92, 0x22, 0xbc, 0x03, 0x60, 0x9f, 0x0a, 0xc9, + 0x38, 0xc5, 0x28, 0xf0, 0x48, 0x28, 0x39, 0x25, 0xc2, 0x5c, 0xd4, 0xe6, 0xab, 0xc5, 0xca, 0xfd, + 0x64, 0x01, 0x3e, 0x04, 0xb7, 0xae, 0x75, 0xea, 0xe1, 0x3e, 0x0a, 0x43, 0x12, 0x98, 0x4b, 0x7a, + 0x2b, 0xdb, 0xfe, 0x35, 0x3e, 0xdb, 0x89, 0x1a, 0x5c, 0x03, 0x73, 0x92, 0x45, 0xde, 0x23, 0x73, + 0xb9, 0x65, 0xec, 0x2c, 0xba, 0x55, 0xc9, 0xa2, 0x47, 0xf0, 0x1d, 0xb0, 0x1e, 0xa3, 0x80, 0xfa, + 0x48, 0x32, 0x2e, 0xbc, 0x88, 0x5d, 0x10, 0xee, 0x61, 0x14, 0x99, 0x2b, 0x5a, 0x07, 0x16, 0x6b, + 0x1d, 0xb5, 0xd4, 0x46, 0x11, 0x7c, 0x1b, 0xac, 0xe6, 0x52, 0x4f, 0x10, 0xa9, 0xd5, 0x57, 0xb5, + 0xfa, 0x72, 0xbe, 0x70, 0x42, 0xa4, 0xd2, 0xdd, 0x02, 0x75, 0x14, 0x04, 0xec, 0x22, 0xa0, 0x42, + 0x9a, 0xb0, 0x55, 0xd9, 0xa9, 0xbb, 0x85, 0x00, 0x6e, 0x82, 0x9a, 0x4f, 0xc2, 0x91, 0x5e, 0x5c, + 0xd3, 0x8b, 0xf9, 0xfb, 0xbd, 0xda, 0x6f, 0xbe, 0xd8, 0x9e, 0xf9, 0xfc, 0x8b, 0xed, 0x19, 0xeb, + 0xef, 0x06, 0xb8, 0xd9, 0xce, 0xb3, 0x34, 0x60, 0x31, 0x0a, 0xbe, 0xcb, 0x6e, 0xb0, 0x0f, 0xea, + 0x42, 0x1d, 0x93, 0xbe, 0x7f, 0xd5, 0x57, 0xb8, 0x7f, 0x35, 0x65, 0xa6, 0x16, 0xac, 0x3f, 0x1a, + 0x60, 0xfd, 0xfe, 0xb3, 0x21, 0x8d, 0x19, 0x46, 0xff, 0x97, 0xe6, 0x75, 0x0c, 0x16, 0x49, 0x09, + 0x4f, 0x98, 0x95, 0x56, 0x65, 0xa7, 0xb1, 0xf7, 0xa6, 0x9d, 0x34, 0x57, 0x3b, 0xef, 0xb9, 0x69, + 0x83, 0xb5, 0xcb, 0xde, 0xdd, 0x71, 0xdb, 0x7b, 0xb3, 0xa6, 0x61, 0xfd, 0xd9, 0x00, 0x9b, 0xaa, + 0x2c, 0x7a, 0xc4, 0x25, 0x17, 0x88, 0xfb, 0x87, 0x24, 0x64, 0x03, 0xf1, 0xad, 0xe3, 0xb4, 0xc0, + 0xa2, 0xaf, 0x91, 0x3c, 0xc9, 0x3c, 0xe4, 0xfb, 0x3a, 0x4e, 0xad, 0xa3, 0x84, 0xa7, 0x6c, 0xdf, + 0xf7, 0xe1, 0x0e, 0x58, 0x29, 0x74, 0xb8, 0xca, 0xa7, 0x3a, 0x66, 0xa5, 0xb6, 0x94, 0xa9, 0xe9, + 0x2c, 0x13, 0xeb, 0x3f, 0x06, 0x58, 0xf9, 0x30, 0x60, 0x5d, 0x14, 0x9c, 0x04, 0x48, 0xf4, 0xd5, + 0x95, 0x18, 0xa9, 0xf4, 0x70, 0x92, 0xf6, 0x22, 0x1d, 0xde, 0xd4, 0xe9, 0x51, 0x66, 0xba, 0x3b, + 0xbe, 0x0f, 0x56, 0xf3, 0xee, 0x90, 0x57, 0x81, 0xde, 0xcd, 0xc1, 0xda, 0xf3, 0xaf, 0xb7, 0x97, + 0xb3, 0x62, 0x6b, 0xeb, 0x8a, 0x38, 0x74, 0x97, 0xf1, 0x98, 0xc0, 0x87, 0x4d, 0xd0, 0xa0, 0x5d, + 0xec, 0x09, 0xf2, 0xcc, 0x0b, 0x87, 0x03, 0x5d, 0x40, 0x55, 0xb7, 0x4e, 0xbb, 0xf8, 0x84, 0x3c, + 0x7b, 0x34, 0x1c, 0xc0, 0x77, 0xc1, 0xeb, 0xd9, 0x60, 0xe0, 0xc5, 0x28, 0xf0, 0x94, 0xbd, 0x3a, + 0x0e, 0xae, 0xeb, 0x69, 0xc1, 0x5d, 0xcb, 0x56, 0xcf, 0x50, 0xa0, 0x9c, 0xed, 0xfb, 0x3e, 0xb7, + 0x5e, 0xcc, 0x81, 0x1b, 0x1d, 0xc4, 0xd1, 0x40, 0xc0, 0x53, 0xb0, 0x2c, 0xc9, 0x20, 0x0a, 0x90, + 0x24, 0x5e, 0xc2, 0x3c, 0xe9, 0x4e, 0x6f, 0x6b, 0x46, 0x2a, 0x93, 0xb8, 0x5d, 0xa2, 0xed, 0x78, + 0xd7, 0x6e, 0x6b, 0xe9, 0x89, 0x44, 0x92, 0xb8, 0x4b, 0x19, 0x46, 0x22, 0x84, 0x77, 0x81, 0x29, + 0xf9, 0x50, 0xc8, 0x82, 0x13, 0x8a, 0x66, 0x98, 0xe4, 0xf2, 0xf5, 0x6c, 0x3d, 0x69, 0xa3, 0x79, + 0x13, 0xbc, 0xba, 0xfd, 0x57, 0xbe, 0x4d, 0xfb, 0x3f, 0x01, 0x6b, 0x8a, 0x3b, 0x27, 0x31, 0xab, + 0xd3, 0x63, 0xae, 0x2a, 0xfb, 0x71, 0xd0, 0x4f, 0x00, 0x8c, 0x05, 0x9e, 0xc4, 0x9c, 0x7b, 0x85, + 0x38, 0x63, 0x81, 0xc7, 0x21, 0x7d, 0xb0, 0x25, 0x54, 0xf1, 0x79, 0x03, 0x22, 0x35, 0x99, 0x44, + 0x01, 0x09, 0xa9, 0xe8, 0x67, 0xe0, 0x37, 0xa6, 0x07, 0xdf, 0xd0, 0x40, 0x1f, 0x2b, 0x1c, 0x37, + 0x83, 0x49, 0xbd, 0xb4, 0x41, 0xf3, 0x6a, 0x2f, 0x79, 0x82, 0xe6, 0x75, 0x82, 0xbe, 0x77, 0x05, + 0x44, 0x9e, 0x25, 0x01, 0xde, 0x2a, 0x91, 0x9e, 0xba, 0xd5, 0x9e, 0xbe, 0x50, 0x1e, 0x27, 0x3d, + 0xc5, 0x0c, 0x28, 0xe1, 0x3f, 0x42, 0x72, 0xe2, 0x4e, 0xbb, 0x87, 0x1a, 0xcd, 0xf2, 0xce, 0xd1, + 0x66, 0x34, 0x4c, 0xa7, 0x1b, 0xab, 0xe0, 0xc6, 0xbc, 0x47, 0xb8, 0x25, 0xac, 0x0f, 0x08, 0x51, + 0xb7, 0xb9, 0xc4, 0x8f, 0x24, 0x62, 0xb8, 0xaf, 0xf9, 0xbb, 0xe2, 0x2e, 0xe5, 0x5c, 0x78, 0x5f, + 0x49, 0x1f, 0x56, 0x6b, 0xb5, 0x95, 0xba, 0xf5, 0x23, 0x50, 0xd7, 0x97, 0x79, 0x1f, 0x9f, 0x0b, + 0xcd, 0x0e, 0xbe, 0xcf, 0x89, 0x10, 0x44, 0x98, 0x46, 0xca, 0x0e, 0x99, 0xc0, 0x92, 0x60, 0xe3, + 0xba, 0x29, 0x50, 0xc0, 0x27, 0x60, 0x3e, 0x22, 0x7a, 0x44, 0xd1, 0x86, 0x8d, 0xbd, 0xf7, 0xec, + 0x29, 0x66, 0x74, 0xfb, 0x3a, 0x40, 0x37, 0x43, 0xb3, 0x78, 0x31, 0x7b, 0x4e, 0x90, 0x8d, 0x80, + 0x67, 0x93, 0x4e, 0x7f, 0xf6, 0x4a, 0x4e, 0x27, 0xf0, 0x0a, 0x9f, 0xb7, 0x41, 0x63, 0x3f, 0xd9, + 0xf6, 0x47, 0x8a, 0x16, 0x2f, 0x1d, 0xcb, 0x42, 0xf9, 0x58, 0x1e, 0x82, 0xa5, 0x94, 0xd0, 0x4f, + 0x99, 0x6e, 0x48, 0xf0, 0xfb, 0x00, 0xa4, 0x93, 0x80, 0x6a, 0x64, 0x49, 0xcb, 0xae, 0xa7, 0x92, + 0x23, 0x7f, 0x8c, 0xeb, 0x66, 0xc7, 0xb8, 0xce, 0x72, 0xc1, 0xf2, 0x99, 0xc0, 0x3f, 0xcf, 0xa6, + 0xbd, 0xc7, 0x91, 0x80, 0xaf, 0x81, 0x1b, 0xea, 0x0e, 0xa5, 0x40, 0x55, 0x77, 0x2e, 0x16, 0xf8, + 0x48, 0x77, 0xed, 0x62, 0xa2, 0x64, 0x91, 0x47, 0x7d, 0x61, 0xce, 0xb6, 0x2a, 0x3b, 0x55, 0x77, + 0x69, 0x58, 0x98, 0x1f, 0xf9, 0xc2, 0xfa, 0x05, 0x68, 0x94, 0x00, 0xe1, 0x12, 0x98, 0xcd, 0xb1, + 0x66, 0xa9, 0x0f, 0xef, 0x81, 0x8d, 0x02, 0x68, 0xbc, 0x0d, 0x27, 0x88, 0x75, 0xf7, 0x66, 0xae, + 0x30, 0xd6, 0x89, 0x85, 0xf5, 0x18, 0xac, 0x1f, 0x15, 0x97, 0x3e, 0x6f, 0xf2, 0x63, 0x3b, 0x34, + 0xc6, 0xd9, 0x7c, 0x0b, 0xd4, 0xf3, 0x5f, 0x52, 0x7a, 0xf7, 0x55, 0xb7, 0x10, 0x58, 0x03, 0xb0, + 0x72, 0x26, 0xf0, 0x09, 0x09, 0xfd, 0x02, 0xec, 0x9a, 0x03, 0x38, 0x98, 0x04, 0x9a, 0x7a, 0x2c, + 0x2f, 0xdc, 0x31, 0xb0, 0x71, 0x56, 0x1e, 0x90, 0x34, 0x01, 0x77, 0x10, 0x3e, 0x27, 0x52, 0x40, + 0x17, 0x54, 0xf5, 0x20, 0x94, 0x54, 0xd6, 0xdd, 0x6b, 0x2b, 0x2b, 0xde, 0xb5, 0xaf, 0x03, 0x39, + 0x44, 0x12, 0xa5, 0x77, 0x57, 0x63, 0x59, 0x3f, 0x04, 0x6b, 0x1f, 0x23, 0x39, 0xe4, 0xc4, 0x1f, + 0xcb, 0xf1, 0x0a, 0xa8, 0xa8, 0xfc, 0x19, 0x3a, 0x7f, 0xea, 0x51, 0xcd, 0x03, 0xe6, 0xfd, 0x4f, + 0x23, 0xc6, 0x25, 0xf1, 0x2f, 0x9d, 0xc8, 0x4b, 0x8e, 0xf7, 0x1c, 0xac, 0xa9, 0xc3, 0x12, 0x24, + 0xf4, 0xbd, 0x7c, 0x9f, 0x49, 0x1e, 0x1b, 0x7b, 0x3f, 0x9d, 0xea, 0x76, 0x4c, 0xba, 0x4b, 0x37, + 0xb0, 0x1a, 0x4f, 0xc8, 0x85, 0xf5, 0x7b, 0x03, 0x98, 0xc7, 0x64, 0xb4, 0x2f, 0x04, 0xed, 0x85, + 0x03, 0x12, 0x4a, 0xd5, 0x03, 0x11, 0x26, 0xea, 0x11, 0xbe, 0x01, 0x16, 0x73, 0xce, 0xd5, 0x54, + 0x6b, 0x68, 0xaa, 0x5d, 0xc8, 0x84, 0xea, 0x82, 0xc1, 0x7b, 0x00, 0x44, 0x9c, 0xc4, 0x1e, 0xf6, + 0xce, 0xc9, 0x28, 0xcd, 0xe2, 0x56, 0x99, 0x42, 0x93, 0xdf, 0xb9, 0x76, 0x67, 0xd8, 0x0d, 0x28, + 0x3e, 0x26, 0x23, 0xb7, 0xa6, 0xf4, 0xdb, 0xc7, 0x64, 0xa4, 0x66, 0x22, 0x3d, 0x1d, 0x6b, 0xde, + 0xab, 0xb8, 0xc9, 0x8b, 0xf5, 0x07, 0x03, 0xdc, 0xcc, 0xd3, 0x91, 0x95, 0x6b, 0x67, 0xd8, 0x55, + 0x16, 0x2f, 0x39, 0xb7, 0x4b, 0xd1, 0xce, 0x5e, 0x11, 0xed, 0xfb, 0x60, 0x21, 0xbf, 0x20, 0x2a, + 0xde, 0xca, 0x14, 0xf1, 0x36, 0x32, 0x8b, 0x63, 0x32, 0xb2, 0x7e, 0x5d, 0x8a, 0xed, 0x60, 0x54, + 0xea, 0x7d, 0xfc, 0x7f, 0xc4, 0x96, 0xbb, 0x2d, 0xc7, 0x86, 0xcb, 0xf6, 0x97, 0x36, 0x50, 0xb9, + 0xbc, 0x01, 0xeb, 0x4f, 0x06, 0x58, 0x2f, 0x7b, 0x15, 0xa7, 0xac, 0xc3, 0x87, 0x21, 0x79, 0x99, + 0xf7, 0xe2, 0xfa, 0xcd, 0x96, 0xaf, 0xdf, 0x13, 0xb0, 0x34, 0x16, 0x94, 0x48, 0x4f, 0xe3, 0x9d, + 0xa9, 0x6a, 0xac, 0xd4, 0x5d, 0xdd, 0xc5, 0xf2, 0x3e, 0x84, 0xf5, 0x17, 0x03, 0xac, 0x66, 0x31, + 0xe6, 0x87, 0x05, 0x7f, 0x0c, 0x60, 0xbe, 0xbd, 0x62, 0x7a, 0x4b, 0x4a, 0x6a, 0x25, 0x5b, 0xc9, + 0x46, 0xb7, 0xa2, 0x34, 0x66, 0x4b, 0xa5, 0x01, 0x3f, 0x02, 0x6b, 0x79, 0xc8, 0x91, 0x4e, 0xd0, + 0xd4, 0x59, 0xcc, 0xe7, 0xd3, 0x5c, 0x64, 0xfd, 0xce, 0x28, 0xe8, 0x30, 0xe1, 0x63, 0xb1, 0x1f, + 0x04, 0xe9, 0x50, 0x0f, 0x23, 0x30, 0x9f, 0x50, 0xbe, 0x48, 0xfb, 0xc7, 0xd6, 0x95, 0xe4, 0x7e, + 0x48, 0xb0, 0xe6, 0xf7, 0xbb, 0xea, 0x8a, 0xfd, 0xed, 0x9b, 0xed, 0xdb, 0x3d, 0x2a, 0xfb, 0xc3, + 0xae, 0x8d, 0xd9, 0xc0, 0x49, 0xbf, 0xd3, 0x24, 0xff, 0xee, 0x08, 0xff, 0xdc, 0x91, 0xa3, 0x88, + 0x88, 0xcc, 0x46, 0xfc, 0xf5, 0xdf, 0xff, 0x78, 0xdb, 0x70, 0x33, 0x37, 0x07, 0x4f, 0xbe, 0x7c, + 0xde, 0x34, 0xbe, 0x7a, 0xde, 0x34, 0xfe, 0xf5, 0xbc, 0x69, 0x7c, 0xf6, 0xa2, 0x39, 0xf3, 0xd5, + 0x8b, 0xe6, 0xcc, 0x3f, 0x5f, 0x34, 0x67, 0x7e, 0xf9, 0xde, 0x65, 0xd0, 0x22, 0x47, 0x77, 0xf2, + 0x2f, 0x63, 0xf1, 0x4f, 0x9c, 0x4f, 0xc7, 0xbf, 0xbb, 0x69, 0x7f, 0xdd, 0x1b, 0xba, 0x9b, 0xbe, + 0xfb, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6d, 0xb7, 0x45, 0x0f, 0xa8, 0x13, 0x00, 0x00, } func (m *ConsumerAdditionProposal) Marshal() (dAtA []byte, err error) { @@ -1623,6 +1702,47 @@ func (m *ConsumerAdditionProposal) MarshalToSizedBuffer(dAtA []byte) (int, error _ = i var l int _ = l + if len(m.Denylist) > 0 { + for iNdEx := len(m.Denylist) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Denylist[iNdEx]) + copy(dAtA[i:], m.Denylist[iNdEx]) + i = encodeVarintProvider(dAtA, i, uint64(len(m.Denylist[iNdEx]))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x9a + } + } + if len(m.Allowlist) > 0 { + for iNdEx := len(m.Allowlist) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Allowlist[iNdEx]) + copy(dAtA[i:], m.Allowlist[iNdEx]) + i = encodeVarintProvider(dAtA, i, uint64(len(m.Allowlist[iNdEx]))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x92 + } + } + if m.ValidatorSetCap != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.ValidatorSetCap)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x88 + } + if m.ValidatorsPowerCap != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.ValidatorsPowerCap)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x80 + } + if m.Top_N != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.Top_N)) + i-- + dAtA[i] = 0x78 + } if len(m.DistributionTransmissionChannel) > 0 { i -= len(m.DistributionTransmissionChannel) copy(dAtA[i:], m.DistributionTransmissionChannel) @@ -2716,6 +2836,43 @@ func (m *ConsumerValidator) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *ConsumerRewardsAllocation) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ConsumerRewardsAllocation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConsumerRewardsAllocation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Rewards) > 0 { + for iNdEx := len(m.Rewards) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Rewards[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProvider(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func encodeVarintProvider(dAtA []byte, offset int, v uint64) int { offset -= sovProvider(v) base := offset @@ -2777,6 +2934,27 @@ func (m *ConsumerAdditionProposal) Size() (n int) { if l > 0 { n += 1 + l + sovProvider(uint64(l)) } + if m.Top_N != 0 { + n += 1 + sovProvider(uint64(m.Top_N)) + } + if m.ValidatorsPowerCap != 0 { + n += 2 + sovProvider(uint64(m.ValidatorsPowerCap)) + } + if m.ValidatorSetCap != 0 { + n += 2 + sovProvider(uint64(m.ValidatorSetCap)) + } + if len(m.Allowlist) > 0 { + for _, s := range m.Allowlist { + l = len(s) + n += 2 + l + sovProvider(uint64(l)) + } + } + if len(m.Denylist) > 0 { + for _, s := range m.Denylist { + l = len(s) + n += 2 + l + sovProvider(uint64(l)) + } + } return n } @@ -3207,6 +3385,21 @@ func (m *ConsumerValidator) Size() (n int) { return n } +func (m *ConsumerRewardsAllocation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Rewards) > 0 { + for _, e := range m.Rewards { + l = e.Size() + n += 1 + l + sovProvider(uint64(l)) + } + } + return n +} + func sovProvider(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -3673,6 +3866,127 @@ func (m *ConsumerAdditionProposal) Unmarshal(dAtA []byte) error { } m.DistributionTransmissionChannel = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 15: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Top_N", wireType) + } + m.Top_N = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Top_N |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 16: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorsPowerCap", wireType) + } + m.ValidatorsPowerCap = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ValidatorsPowerCap |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 17: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorSetCap", wireType) + } + m.ValidatorSetCap = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ValidatorSetCap |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 18: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Allowlist", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Allowlist = append(m.Allowlist, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 19: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denylist", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denylist = append(m.Denylist, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipProvider(dAtA[iNdEx:]) @@ -6639,6 +6953,90 @@ func (m *ConsumerValidator) Unmarshal(dAtA []byte) error { } return nil } +func (m *ConsumerRewardsAllocation) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ConsumerRewardsAllocation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConsumerRewardsAllocation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rewards", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Rewards = append(m.Rewards, types2.DecCoin{}) + if err := m.Rewards[len(m.Rewards)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProvider(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProvider + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipProvider(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/ccv/provider/types/query.pb.go b/x/ccv/provider/types/query.pb.go index 84d4ff8e34..099c2be2db 100644 --- a/x/ccv/provider/types/query.pb.go +++ b/x/ccv/provider/types/query.pb.go @@ -7,11 +7,13 @@ import ( context "context" fmt "fmt" crypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" proto "github.com/cosmos/gogoproto/proto" github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" - types "github.com/cosmos/interchain-security/v4/x/ccv/types" + types "github.com/cosmos/interchain-security/v5/x/ccv/types" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -374,6 +376,8 @@ func (m *QueryConsumerChainStopProposalsResponse) GetProposals() *ConsumerRemova type Chain struct { ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` + // If chain with `chainID` is a Top-N chain, i.e., enforces at least one validator to validate chain `chainID` + Top_N uint32 `protobuf:"varint,3,opt,name=top_N,json=topN,proto3" json:"top_N,omitempty"` } func (m *Chain) Reset() { *m = Chain{} } @@ -423,6 +427,13 @@ func (m *Chain) GetClientId() string { return "" } +func (m *Chain) GetTop_N() uint32 { + if m != nil { + return m.Top_N + } + return 0 +} + type QueryValidatorConsumerAddrRequest struct { // The id of the consumer chain ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` @@ -1013,9 +1024,9 @@ func (m *QueryAllPairsValConAddrByConsumerChainIDResponse) GetPairValConAddr() [ type PairValConAddrProviderAndConsumer struct { // The consensus address of the validator on the provider chain - ProviderAddress string `protobuf:"bytes,1,opt,name=provider_address,json=providerAddress,proto3" json:"provider_address,omitempty" yaml:"address"` + ProviderAddress string `protobuf:"bytes,1,opt,name=provider_address,json=providerAddress,proto3" json:"provider_address,omitempty" yaml:"provider_address"` // The consensus address of the validator on the consumer chain - ConsumerAddress string `protobuf:"bytes,2,opt,name=consumer_address,json=consumerAddress,proto3" json:"consumer_address,omitempty" yaml:"address"` + ConsumerAddress string `protobuf:"bytes,2,opt,name=consumer_address,json=consumerAddress,proto3" json:"consumer_address,omitempty" yaml:"consumer_address"` ConsumerKey *crypto.PublicKey `protobuf:"bytes,3,opt,name=consumer_key,json=consumerKey,proto3" json:"consumer_key,omitempty"` } @@ -1153,6 +1164,299 @@ func (m *QueryParamsResponse) GetParams() Params { return Params{} } +type QueryConsumerChainOptedInValidatorsRequest struct { + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` +} + +func (m *QueryConsumerChainOptedInValidatorsRequest) Reset() { + *m = QueryConsumerChainOptedInValidatorsRequest{} +} +func (m *QueryConsumerChainOptedInValidatorsRequest) String() string { + return proto.CompactTextString(m) +} +func (*QueryConsumerChainOptedInValidatorsRequest) ProtoMessage() {} +func (*QueryConsumerChainOptedInValidatorsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_422512d7b7586cd7, []int{25} +} +func (m *QueryConsumerChainOptedInValidatorsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsumerChainOptedInValidatorsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsumerChainOptedInValidatorsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryConsumerChainOptedInValidatorsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsumerChainOptedInValidatorsRequest.Merge(m, src) +} +func (m *QueryConsumerChainOptedInValidatorsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryConsumerChainOptedInValidatorsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsumerChainOptedInValidatorsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsumerChainOptedInValidatorsRequest proto.InternalMessageInfo + +func (m *QueryConsumerChainOptedInValidatorsRequest) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +type QueryConsumerChainOptedInValidatorsResponse struct { + // The consensus addresses of the validators on the provider chain + ValidatorsProviderAddresses []string `protobuf:"bytes,1,rep,name=validators_provider_addresses,json=validatorsProviderAddresses,proto3" json:"validators_provider_addresses,omitempty"` +} + +func (m *QueryConsumerChainOptedInValidatorsResponse) Reset() { + *m = QueryConsumerChainOptedInValidatorsResponse{} +} +func (m *QueryConsumerChainOptedInValidatorsResponse) String() string { + return proto.CompactTextString(m) +} +func (*QueryConsumerChainOptedInValidatorsResponse) ProtoMessage() {} +func (*QueryConsumerChainOptedInValidatorsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_422512d7b7586cd7, []int{26} +} +func (m *QueryConsumerChainOptedInValidatorsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsumerChainOptedInValidatorsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsumerChainOptedInValidatorsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryConsumerChainOptedInValidatorsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsumerChainOptedInValidatorsResponse.Merge(m, src) +} +func (m *QueryConsumerChainOptedInValidatorsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryConsumerChainOptedInValidatorsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsumerChainOptedInValidatorsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsumerChainOptedInValidatorsResponse proto.InternalMessageInfo + +func (m *QueryConsumerChainOptedInValidatorsResponse) GetValidatorsProviderAddresses() []string { + if m != nil { + return m.ValidatorsProviderAddresses + } + return nil +} + +type QueryConsumerChainsValidatorHasToValidateRequest struct { + // The consensus address of the validator on the provider chain + ProviderAddress string `protobuf:"bytes,1,opt,name=provider_address,json=providerAddress,proto3" json:"provider_address,omitempty" yaml:"address"` +} + +func (m *QueryConsumerChainsValidatorHasToValidateRequest) Reset() { + *m = QueryConsumerChainsValidatorHasToValidateRequest{} +} +func (m *QueryConsumerChainsValidatorHasToValidateRequest) String() string { + return proto.CompactTextString(m) +} +func (*QueryConsumerChainsValidatorHasToValidateRequest) ProtoMessage() {} +func (*QueryConsumerChainsValidatorHasToValidateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_422512d7b7586cd7, []int{27} +} +func (m *QueryConsumerChainsValidatorHasToValidateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsumerChainsValidatorHasToValidateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsumerChainsValidatorHasToValidateRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryConsumerChainsValidatorHasToValidateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsumerChainsValidatorHasToValidateRequest.Merge(m, src) +} +func (m *QueryConsumerChainsValidatorHasToValidateRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryConsumerChainsValidatorHasToValidateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsumerChainsValidatorHasToValidateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsumerChainsValidatorHasToValidateRequest proto.InternalMessageInfo + +func (m *QueryConsumerChainsValidatorHasToValidateRequest) GetProviderAddress() string { + if m != nil { + return m.ProviderAddress + } + return "" +} + +type QueryConsumerChainsValidatorHasToValidateResponse struct { + ConsumerChainIds []string `protobuf:"bytes,1,rep,name=consumer_chain_ids,json=consumerChainIds,proto3" json:"consumer_chain_ids,omitempty"` +} + +func (m *QueryConsumerChainsValidatorHasToValidateResponse) Reset() { + *m = QueryConsumerChainsValidatorHasToValidateResponse{} +} +func (m *QueryConsumerChainsValidatorHasToValidateResponse) String() string { + return proto.CompactTextString(m) +} +func (*QueryConsumerChainsValidatorHasToValidateResponse) ProtoMessage() {} +func (*QueryConsumerChainsValidatorHasToValidateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_422512d7b7586cd7, []int{28} +} +func (m *QueryConsumerChainsValidatorHasToValidateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsumerChainsValidatorHasToValidateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsumerChainsValidatorHasToValidateResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryConsumerChainsValidatorHasToValidateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsumerChainsValidatorHasToValidateResponse.Merge(m, src) +} +func (m *QueryConsumerChainsValidatorHasToValidateResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryConsumerChainsValidatorHasToValidateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsumerChainsValidatorHasToValidateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsumerChainsValidatorHasToValidateResponse proto.InternalMessageInfo + +func (m *QueryConsumerChainsValidatorHasToValidateResponse) GetConsumerChainIds() []string { + if m != nil { + return m.ConsumerChainIds + } + return nil +} + +type QueryValidatorConsumerCommissionRateRequest struct { + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + // The consensus address of the validator on the provider chain + ProviderAddress string `protobuf:"bytes,2,opt,name=provider_address,json=providerAddress,proto3" json:"provider_address,omitempty" yaml:"address"` +} + +func (m *QueryValidatorConsumerCommissionRateRequest) Reset() { + *m = QueryValidatorConsumerCommissionRateRequest{} +} +func (m *QueryValidatorConsumerCommissionRateRequest) String() string { + return proto.CompactTextString(m) +} +func (*QueryValidatorConsumerCommissionRateRequest) ProtoMessage() {} +func (*QueryValidatorConsumerCommissionRateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_422512d7b7586cd7, []int{29} +} +func (m *QueryValidatorConsumerCommissionRateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorConsumerCommissionRateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorConsumerCommissionRateRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryValidatorConsumerCommissionRateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorConsumerCommissionRateRequest.Merge(m, src) +} +func (m *QueryValidatorConsumerCommissionRateRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorConsumerCommissionRateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorConsumerCommissionRateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorConsumerCommissionRateRequest proto.InternalMessageInfo + +func (m *QueryValidatorConsumerCommissionRateRequest) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +func (m *QueryValidatorConsumerCommissionRateRequest) GetProviderAddress() string { + if m != nil { + return m.ProviderAddress + } + return "" +} + +type QueryValidatorConsumerCommissionRateResponse struct { + // The rate to charge delegators on the consumer chain, as a fraction + Rate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=rate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"rate"` +} + +func (m *QueryValidatorConsumerCommissionRateResponse) Reset() { + *m = QueryValidatorConsumerCommissionRateResponse{} +} +func (m *QueryValidatorConsumerCommissionRateResponse) String() string { + return proto.CompactTextString(m) +} +func (*QueryValidatorConsumerCommissionRateResponse) ProtoMessage() {} +func (*QueryValidatorConsumerCommissionRateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_422512d7b7586cd7, []int{30} +} +func (m *QueryValidatorConsumerCommissionRateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorConsumerCommissionRateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorConsumerCommissionRateResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryValidatorConsumerCommissionRateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorConsumerCommissionRateResponse.Merge(m, src) +} +func (m *QueryValidatorConsumerCommissionRateResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorConsumerCommissionRateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorConsumerCommissionRateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorConsumerCommissionRateResponse proto.InternalMessageInfo + type QueryOldestUnconfirmedVscRequest struct { ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` } @@ -1161,7 +1465,7 @@ func (m *QueryOldestUnconfirmedVscRequest) Reset() { *m = QueryOldestUnc func (m *QueryOldestUnconfirmedVscRequest) String() string { return proto.CompactTextString(m) } func (*QueryOldestUnconfirmedVscRequest) ProtoMessage() {} func (*QueryOldestUnconfirmedVscRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_422512d7b7586cd7, []int{25} + return fileDescriptor_422512d7b7586cd7, []int{31} } func (m *QueryOldestUnconfirmedVscRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1205,7 +1509,7 @@ func (m *QueryOldestUnconfirmedVscResponse) Reset() { *m = QueryOldestUn func (m *QueryOldestUnconfirmedVscResponse) String() string { return proto.CompactTextString(m) } func (*QueryOldestUnconfirmedVscResponse) ProtoMessage() {} func (*QueryOldestUnconfirmedVscResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_422512d7b7586cd7, []int{26} + return fileDescriptor_422512d7b7586cd7, []int{32} } func (m *QueryOldestUnconfirmedVscResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1267,6 +1571,12 @@ func init() { proto.RegisterType((*PairValConAddrProviderAndConsumer)(nil), "interchain_security.ccv.provider.v1.PairValConAddrProviderAndConsumer") proto.RegisterType((*QueryParamsRequest)(nil), "interchain_security.ccv.provider.v1.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "interchain_security.ccv.provider.v1.QueryParamsResponse") + proto.RegisterType((*QueryConsumerChainOptedInValidatorsRequest)(nil), "interchain_security.ccv.provider.v1.QueryConsumerChainOptedInValidatorsRequest") + proto.RegisterType((*QueryConsumerChainOptedInValidatorsResponse)(nil), "interchain_security.ccv.provider.v1.QueryConsumerChainOptedInValidatorsResponse") + proto.RegisterType((*QueryConsumerChainsValidatorHasToValidateRequest)(nil), "interchain_security.ccv.provider.v1.QueryConsumerChainsValidatorHasToValidateRequest") + proto.RegisterType((*QueryConsumerChainsValidatorHasToValidateResponse)(nil), "interchain_security.ccv.provider.v1.QueryConsumerChainsValidatorHasToValidateResponse") + proto.RegisterType((*QueryValidatorConsumerCommissionRateRequest)(nil), "interchain_security.ccv.provider.v1.QueryValidatorConsumerCommissionRateRequest") + proto.RegisterType((*QueryValidatorConsumerCommissionRateResponse)(nil), "interchain_security.ccv.provider.v1.QueryValidatorConsumerCommissionRateResponse") proto.RegisterType((*QueryOldestUnconfirmedVscRequest)(nil), "interchain_security.ccv.provider.v1.QueryOldestUnconfirmedVscRequest") proto.RegisterType((*QueryOldestUnconfirmedVscResponse)(nil), "interchain_security.ccv.provider.v1.QueryOldestUnconfirmedVscResponse") } @@ -1276,101 +1586,122 @@ func init() { } var fileDescriptor_422512d7b7586cd7 = []byte{ - // 1501 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0xcf, 0x73, 0xdb, 0x44, - 0x14, 0x8e, 0x92, 0x36, 0x24, 0x9b, 0xfe, 0x62, 0x5b, 0x4a, 0xaa, 0x06, 0xbb, 0x55, 0x07, 0x48, - 0x5b, 0x90, 0x12, 0x97, 0x0e, 0xfd, 0x41, 0x9a, 0xda, 0x49, 0x5a, 0x3c, 0x69, 0xa7, 0x41, 0x6d, - 0xc3, 0x0c, 0x30, 0xa8, 0x1b, 0x69, 0xeb, 0x68, 0x2a, 0x6b, 0xd5, 0x5d, 0xd9, 0xad, 0xa7, 0xc3, - 0xa1, 0x1c, 0xa0, 0x27, 0xa6, 0x33, 0xc0, 0xbd, 0x17, 0xfe, 0x01, 0xfe, 0x8a, 0x72, 0xa2, 0x4c, - 0x2f, 0x9c, 0x0a, 0x93, 0x72, 0x60, 0x38, 0x31, 0x5c, 0x38, 0x31, 0xc3, 0x68, 0xb5, 0x92, 0x2d, - 0x5b, 0xb1, 0x65, 0x27, 0x37, 0x7b, 0xb5, 0xef, 0x7b, 0xdf, 0xf7, 0xf4, 0xf6, 0xed, 0x67, 0x03, - 0xcd, 0x76, 0x7d, 0x4c, 0xcd, 0x75, 0x64, 0xbb, 0x06, 0xc3, 0x66, 0x8d, 0xda, 0x7e, 0x43, 0x33, - 0xcd, 0xba, 0xe6, 0x51, 0x52, 0xb7, 0x2d, 0x4c, 0xb5, 0xfa, 0xac, 0x76, 0xb7, 0x86, 0x69, 0x43, - 0xf5, 0x28, 0xf1, 0x09, 0x3c, 0x96, 0x12, 0xa0, 0x9a, 0x66, 0x5d, 0x8d, 0x02, 0xd4, 0xfa, 0xac, - 0x3c, 0x55, 0x21, 0xa4, 0xe2, 0x60, 0x0d, 0x79, 0xb6, 0x86, 0x5c, 0x97, 0xf8, 0xc8, 0xb7, 0x89, - 0xcb, 0x42, 0x08, 0xf9, 0x40, 0x85, 0x54, 0x08, 0xff, 0xa8, 0x05, 0x9f, 0xc4, 0x6a, 0x5e, 0xc4, - 0xf0, 0x6f, 0x6b, 0xb5, 0xdb, 0x9a, 0x6f, 0x57, 0x31, 0xf3, 0x51, 0xd5, 0x13, 0x1b, 0x0a, 0x59, - 0xa8, 0xc6, 0x2c, 0xc2, 0x98, 0x99, 0xcd, 0x62, 0xea, 0xb3, 0x1a, 0x5b, 0x47, 0x14, 0x5b, 0x86, - 0x49, 0x5c, 0x56, 0xab, 0xc6, 0x11, 0x6f, 0x76, 0x89, 0xb8, 0x67, 0x53, 0x2c, 0xb6, 0x4d, 0xf9, - 0xd8, 0xb5, 0x30, 0xad, 0xda, 0xae, 0xaf, 0x99, 0xb4, 0xe1, 0xf9, 0x44, 0xbb, 0x83, 0x1b, 0x42, - 0xa1, 0x72, 0x06, 0x1c, 0xfe, 0x28, 0xa8, 0xd9, 0x82, 0xc0, 0xbe, 0x8c, 0x5d, 0xcc, 0x6c, 0xa6, - 0xe3, 0xbb, 0x35, 0xcc, 0x7c, 0x78, 0x08, 0x8c, 0x85, 0x09, 0x6c, 0x6b, 0x52, 0x3a, 0x22, 0x4d, - 0x8f, 0xeb, 0xaf, 0xf0, 0xef, 0x65, 0x4b, 0x79, 0x00, 0xa6, 0xd2, 0x23, 0x99, 0x47, 0x5c, 0x86, - 0xe1, 0xa7, 0x60, 0x77, 0x25, 0x5c, 0x32, 0x98, 0x8f, 0x7c, 0xcc, 0xe3, 0x27, 0x0a, 0x33, 0xea, - 0x66, 0xaf, 0xa5, 0x3e, 0xab, 0xb6, 0x61, 0x5d, 0x0f, 0xe2, 0x4a, 0x3b, 0x9e, 0xbe, 0xc8, 0x0f, - 0xe9, 0xbb, 0x2a, 0x2d, 0x6b, 0xca, 0x14, 0x90, 0x13, 0xc9, 0x17, 0x02, 0xb8, 0x88, 0xb5, 0x82, - 0xda, 0x44, 0x45, 0x4f, 0x05, 0xb3, 0x12, 0x18, 0xe5, 0xe9, 0xd9, 0xa4, 0x74, 0x64, 0x64, 0x7a, - 0xa2, 0x70, 0x42, 0xcd, 0xd0, 0x29, 0x2a, 0x07, 0xd1, 0x45, 0xa4, 0x72, 0x1c, 0xbc, 0xdd, 0x99, - 0xe2, 0xba, 0x8f, 0xa8, 0xbf, 0x42, 0x89, 0x47, 0x18, 0x72, 0x62, 0x36, 0x8f, 0x24, 0x30, 0xdd, - 0x7b, 0xaf, 0xe0, 0xf6, 0x19, 0x18, 0xf7, 0xa2, 0x45, 0x51, 0xb1, 0x0b, 0xd9, 0xe8, 0x09, 0xf0, - 0xa2, 0x65, 0xd9, 0x41, 0x0b, 0x37, 0xa1, 0x9b, 0x80, 0xca, 0x34, 0x78, 0x2b, 0x8d, 0x09, 0xf1, - 0x3a, 0x48, 0x7f, 0x25, 0xa5, 0x0b, 0x4c, 0x6c, 0x8d, 0xdf, 0x74, 0x07, 0xe7, 0xb9, 0xbe, 0x38, - 0xeb, 0xb8, 0x4a, 0xea, 0xc8, 0x49, 0xa5, 0x3c, 0x0f, 0x76, 0xf2, 0xd4, 0x5d, 0x5a, 0x11, 0x1e, - 0x06, 0xe3, 0xa6, 0x63, 0x63, 0xd7, 0x0f, 0x9e, 0x0d, 0xf3, 0x67, 0x63, 0xe1, 0x42, 0xd9, 0x52, - 0xbe, 0x96, 0xc0, 0x51, 0xae, 0x64, 0x15, 0x39, 0xb6, 0x85, 0x7c, 0x42, 0x5b, 0x4a, 0x45, 0x7b, - 0x37, 0x3a, 0x9c, 0x03, 0xfb, 0x22, 0xd2, 0x06, 0xb2, 0x2c, 0x8a, 0x19, 0x0b, 0x93, 0x94, 0xe0, - 0x3f, 0x2f, 0xf2, 0x7b, 0x1a, 0xa8, 0xea, 0x9c, 0x53, 0xc4, 0x03, 0x45, 0xdf, 0x1b, 0xed, 0x2d, - 0x86, 0x2b, 0xe7, 0xc6, 0x1e, 0x3d, 0xc9, 0x0f, 0xfd, 0xf9, 0x24, 0x3f, 0xa4, 0x5c, 0x03, 0x4a, - 0x37, 0x22, 0xa2, 0x9a, 0xc7, 0xc1, 0xbe, 0xe8, 0xa0, 0xc7, 0xe9, 0x42, 0x46, 0x7b, 0xcd, 0x96, - 0xfd, 0x41, 0xb2, 0x4e, 0x69, 0x2b, 0x2d, 0xc9, 0xb3, 0x49, 0xeb, 0xc8, 0xd5, 0x45, 0x5a, 0x5b, - 0xfe, 0x6e, 0xd2, 0x92, 0x44, 0x9a, 0xd2, 0x3a, 0x2a, 0x29, 0xa4, 0xb5, 0x55, 0x4d, 0x39, 0x0c, - 0x0e, 0x71, 0xc0, 0x1b, 0xeb, 0x94, 0xf8, 0xbe, 0x83, 0xf9, 0xb1, 0x8f, 0x9a, 0xf3, 0x17, 0x49, - 0x1c, 0xff, 0xb6, 0xa7, 0x22, 0x4d, 0x1e, 0x4c, 0x30, 0x07, 0xb1, 0x75, 0xa3, 0x8a, 0x7d, 0x4c, - 0x79, 0x86, 0x11, 0x1d, 0xf0, 0xa5, 0xab, 0xc1, 0x0a, 0x2c, 0x80, 0xd7, 0x5a, 0x36, 0x18, 0xc8, - 0x71, 0xc8, 0x3d, 0xe4, 0x9a, 0x98, 0x6b, 0x1f, 0xd1, 0xf7, 0x37, 0xb7, 0x16, 0xa3, 0x47, 0xf0, - 0x73, 0x30, 0xe9, 0xe2, 0xfb, 0xbe, 0x41, 0xb1, 0xe7, 0x60, 0xd7, 0x66, 0xeb, 0x86, 0x89, 0x5c, - 0x2b, 0x10, 0x8b, 0x27, 0x47, 0x78, 0xcf, 0xcb, 0x6a, 0x78, 0x2f, 0xa8, 0xd1, 0xbd, 0xa0, 0xde, - 0x88, 0xee, 0x85, 0xd2, 0x58, 0x30, 0xc3, 0x1e, 0xff, 0x96, 0x97, 0xf4, 0x83, 0x01, 0x8a, 0x1e, - 0x81, 0x2c, 0x44, 0x18, 0xca, 0x3b, 0xe0, 0x04, 0x97, 0xa4, 0xe3, 0x8a, 0xcd, 0x7c, 0x4c, 0xb1, - 0xd5, 0x3c, 0x1d, 0xf7, 0x10, 0xb5, 0x16, 0xb1, 0x4b, 0xaa, 0xf1, 0xf1, 0x5c, 0x02, 0x27, 0x33, - 0xed, 0x16, 0x15, 0x39, 0x08, 0x46, 0x2d, 0xbe, 0xc2, 0x27, 0xde, 0xb8, 0x2e, 0xbe, 0x29, 0x39, - 0x31, 0xc3, 0xc3, 0x93, 0x87, 0x2d, 0x7e, 0xd2, 0xca, 0x8b, 0x71, 0x9a, 0x87, 0x12, 0x78, 0x63, - 0x93, 0x0d, 0x02, 0xf9, 0x16, 0xd8, 0xe3, 0xb5, 0x3e, 0x8b, 0x66, 0x6a, 0x21, 0xd3, 0x00, 0x48, - 0xc0, 0x8a, 0x41, 0xdf, 0x86, 0xa7, 0x94, 0xc1, 0xee, 0xc4, 0x36, 0x38, 0x09, 0x44, 0xff, 0x2e, - 0x26, 0xdb, 0x79, 0x11, 0xe6, 0x00, 0x88, 0x06, 0x47, 0x79, 0x91, 0xbf, 0xcc, 0x1d, 0x7a, 0xcb, - 0x8a, 0x72, 0x05, 0x68, 0x5c, 0x4d, 0xd1, 0x71, 0x56, 0x90, 0x4d, 0xd9, 0x2a, 0x72, 0x16, 0x88, - 0x1b, 0xb4, 0x5c, 0x29, 0x39, 0xe7, 0xca, 0x8b, 0x19, 0x2e, 0xc0, 0x1f, 0x24, 0x30, 0x93, 0x1d, - 0x4e, 0xd4, 0xeb, 0x2e, 0x78, 0xd5, 0x43, 0x36, 0x35, 0xea, 0xc8, 0x09, 0xee, 0x73, 0x7e, 0x0c, - 0x44, 0xc9, 0x2e, 0x65, 0x2b, 0x19, 0xb2, 0x69, 0x33, 0x51, 0x7c, 0xcc, 0xdc, 0x66, 0x03, 0xec, - 0xf1, 0x12, 0x5b, 0x94, 0x0d, 0x09, 0x1c, 0xed, 0x19, 0x95, 0x3a, 0xe5, 0xa4, 0xcc, 0x53, 0x6e, - 0x8b, 0x93, 0x04, 0xce, 0x83, 0x5d, 0x71, 0xf8, 0x1d, 0xdc, 0x10, 0x27, 0x6a, 0x4a, 0x6d, 0x7a, - 0x17, 0x35, 0xf4, 0x2e, 0xea, 0x4a, 0x6d, 0xcd, 0xb1, 0xcd, 0x65, 0xdc, 0xd0, 0x27, 0xa2, 0x88, - 0x65, 0xdc, 0x50, 0x0e, 0x00, 0x18, 0x36, 0x2a, 0xa2, 0xa8, 0x79, 0x4c, 0x6e, 0x81, 0xfd, 0x89, - 0x55, 0xf1, 0x12, 0xca, 0x60, 0xd4, 0xe3, 0x2b, 0xe2, 0xb6, 0x3a, 0x99, 0xb1, 0xf2, 0x41, 0x88, - 0xe8, 0x52, 0x01, 0xa0, 0xcc, 0x81, 0x23, 0x3c, 0xc3, 0x35, 0xc7, 0xc2, 0xcc, 0xbf, 0xe9, 0x9a, - 0xc4, 0xbd, 0x6d, 0xd3, 0x2a, 0xb6, 0x56, 0x99, 0x99, 0xa1, 0x87, 0xbe, 0x89, 0x26, 0x78, 0x7a, - 0xbc, 0xe0, 0x6b, 0x03, 0x58, 0x67, 0xa6, 0xc1, 0xb0, 0x6b, 0x19, 0xb1, 0xd7, 0x14, 0xdc, 0x4f, - 0x67, 0xe2, 0xbe, 0xca, 0xcc, 0xeb, 0xd8, 0xb5, 0x9a, 0x03, 0x29, 0x54, 0xb1, 0xaf, 0xde, 0xb6, - 0x5e, 0xf8, 0xe9, 0x20, 0xd8, 0xc9, 0x09, 0xc1, 0x0d, 0x09, 0x1c, 0x48, 0x33, 0x78, 0xf0, 0x62, - 0xa6, 0x8c, 0x5d, 0x5c, 0xa5, 0x5c, 0xdc, 0x02, 0x42, 0x58, 0x12, 0x65, 0xe9, 0xcb, 0xe7, 0x7f, - 0x7c, 0x3b, 0x3c, 0x0f, 0xe7, 0x7a, 0xff, 0x2c, 0x88, 0x1b, 0x4b, 0x38, 0x48, 0xed, 0x41, 0xf4, - 0x36, 0xbe, 0x80, 0xcf, 0x25, 0xd1, 0x21, 0x49, 0xab, 0x08, 0xe7, 0xfb, 0x67, 0x98, 0xb0, 0xa0, - 0xf2, 0xc5, 0xc1, 0x01, 0x84, 0xc2, 0xb3, 0x5c, 0xe1, 0x29, 0x38, 0xdb, 0x87, 0xc2, 0xd0, 0x9c, - 0xc2, 0x87, 0xc3, 0x60, 0x72, 0x13, 0xc7, 0xc9, 0xe0, 0x95, 0x01, 0x99, 0xa5, 0x9a, 0x5b, 0xf9, - 0xea, 0x36, 0xa1, 0x09, 0xd1, 0x1f, 0x72, 0xd1, 0x25, 0x78, 0xb1, 0x5f, 0xd1, 0xc1, 0x6f, 0x0c, - 0xea, 0x1b, 0xb1, 0x6f, 0x84, 0xff, 0x49, 0xe0, 0xf5, 0x74, 0x03, 0xcb, 0xe0, 0xf2, 0xc0, 0xa4, - 0x3b, 0x9d, 0xb2, 0x7c, 0x65, 0x7b, 0xc0, 0x44, 0x01, 0x2e, 0xf3, 0x02, 0x14, 0xe1, 0xfc, 0x00, - 0x05, 0x20, 0x5e, 0x8b, 0xfe, 0xbf, 0x23, 0x8f, 0x94, 0xea, 0x36, 0xe1, 0xa5, 0xec, 0xac, 0xbb, - 0xf9, 0x66, 0xf9, 0xf2, 0x96, 0x71, 0x84, 0xf0, 0x22, 0x17, 0x7e, 0x1e, 0x9e, 0xcd, 0xf0, 0x3b, - 0x3f, 0x02, 0x32, 0x12, 0x57, 0x4e, 0x8a, 0xe4, 0x56, 0x17, 0x3a, 0x90, 0xe4, 0x14, 0x3f, 0x3d, - 0x90, 0xe4, 0x34, 0x3b, 0x3c, 0x98, 0xe4, 0xc4, 0x25, 0x0d, 0x7f, 0x96, 0xc4, 0xbd, 0x97, 0x70, - 0xc2, 0xf0, 0x42, 0x76, 0x8a, 0x69, 0x06, 0x5b, 0x9e, 0x1f, 0x38, 0x5e, 0x48, 0x3b, 0xc3, 0xa5, - 0x15, 0xe0, 0x4c, 0x6f, 0x69, 0xbe, 0x00, 0x08, 0xff, 0x25, 0x80, 0xdf, 0x0f, 0x83, 0x63, 0x19, - 0xac, 0x2d, 0xbc, 0x96, 0x9d, 0x62, 0x26, 0x4b, 0x2d, 0xaf, 0x6c, 0x1f, 0xa0, 0x28, 0xc2, 0x32, - 0x2f, 0xc2, 0x12, 0x5c, 0xe8, 0x5d, 0x04, 0x1a, 0x23, 0x36, 0x7b, 0x9a, 0x72, 0x4c, 0x23, 0xb4, - 0xea, 0xf0, 0xaf, 0x0e, 0x2b, 0x9e, 0x74, 0x98, 0x0c, 0xf6, 0x71, 0xab, 0x6e, 0xe2, 0xf7, 0xe5, - 0xd2, 0x56, 0x20, 0x84, 0xea, 0x12, 0x57, 0xfd, 0x01, 0x3c, 0xd7, 0x5b, 0x75, 0xe4, 0xf4, 0x8d, - 0xf6, 0x0b, 0xec, 0xbb, 0x61, 0xf1, 0x97, 0x49, 0x06, 0x6b, 0x0d, 0x6f, 0x64, 0x27, 0x9d, 0xdd, - 0xf8, 0xcb, 0x37, 0xb7, 0x19, 0x55, 0x54, 0xe7, 0x3c, 0xaf, 0xce, 0x69, 0x78, 0xaa, 0xef, 0xf9, - 0x6e, 0x5b, 0xf0, 0x47, 0x09, 0x4c, 0xb4, 0xf8, 0x59, 0xf8, 0x7e, 0x1f, 0xaf, 0xab, 0xd5, 0x17, - 0xcb, 0x67, 0xfa, 0x0f, 0x14, 0xfc, 0x67, 0x38, 0xff, 0x13, 0x70, 0x3a, 0xc3, 0xdb, 0x0d, 0x49, - 0xfe, 0x2b, 0x89, 0x9f, 0xf2, 0x69, 0x16, 0x17, 0x2e, 0x65, 0x67, 0xd2, 0xc5, 0x62, 0xcb, 0x97, - 0xb6, 0x0a, 0xd3, 0xff, 0x91, 0x25, 0x1c, 0xc7, 0xa8, 0x35, 0x81, 0x8c, 0x3a, 0x33, 0x5b, 0xcc, - 0x65, 0xe9, 0xe3, 0xa7, 0x1b, 0x39, 0xe9, 0xd9, 0x46, 0x4e, 0xfa, 0x7d, 0x23, 0x27, 0x3d, 0x7e, - 0x99, 0x1b, 0x7a, 0xf6, 0x32, 0x37, 0xf4, 0xeb, 0xcb, 0xdc, 0xd0, 0x27, 0x73, 0x15, 0xdb, 0x5f, - 0xaf, 0xad, 0xa9, 0x26, 0xa9, 0x6a, 0x26, 0x61, 0x55, 0xc2, 0x5a, 0xf2, 0xbd, 0x1b, 0xe7, 0xab, - 0xbf, 0xa7, 0xdd, 0x6f, 0x1b, 0x96, 0x0d, 0x0f, 0xb3, 0xb5, 0x51, 0xfe, 0x0f, 0xc3, 0xa9, 0xff, - 0x03, 0x00, 0x00, 0xff, 0xff, 0xe2, 0x62, 0xc2, 0x43, 0x13, 0x17, 0x00, 0x00, + // 1834 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcd, 0x6f, 0xdc, 0x5a, + 0x15, 0x8f, 0x93, 0x34, 0x24, 0x37, 0xaf, 0x7d, 0xe5, 0xb6, 0xbc, 0x97, 0x3a, 0xe9, 0x4c, 0x9e, + 0x1f, 0x3c, 0xd2, 0x2f, 0x3b, 0x99, 0xf2, 0x44, 0xbf, 0xd2, 0x34, 0x93, 0x49, 0xd2, 0x51, 0xda, + 0x66, 0xea, 0xa6, 0x01, 0x15, 0x84, 0xeb, 0xd8, 0xb7, 0x13, 0xab, 0x1e, 0x5f, 0xc7, 0xd7, 0x33, + 0xed, 0xa8, 0xaa, 0x44, 0x59, 0x40, 0x57, 0xa8, 0xe2, 0x63, 0xdf, 0x0d, 0x4b, 0x36, 0x08, 0xf1, + 0x2f, 0xd0, 0x1d, 0x85, 0x6e, 0x10, 0x8b, 0x80, 0x52, 0x16, 0x88, 0x15, 0xaa, 0x90, 0x58, 0x21, + 0x21, 0x5f, 0x5f, 0x7f, 0xcd, 0x38, 0x33, 0x9e, 0x49, 0x58, 0x25, 0x73, 0x7d, 0xef, 0xef, 0x9c, + 0xdf, 0xf1, 0x39, 0xe7, 0x9e, 0x9f, 0x81, 0x64, 0x58, 0x2e, 0x72, 0xb4, 0x6d, 0xd5, 0xb0, 0x14, + 0x82, 0xb4, 0xba, 0x63, 0xb8, 0x4d, 0x49, 0xd3, 0x1a, 0x92, 0xed, 0xe0, 0x86, 0xa1, 0x23, 0x47, + 0x6a, 0xcc, 0x49, 0x3b, 0x75, 0xe4, 0x34, 0x45, 0xdb, 0xc1, 0x2e, 0x86, 0x9f, 0xa7, 0x1c, 0x10, + 0x35, 0xad, 0x21, 0x06, 0x07, 0xc4, 0xc6, 0x1c, 0x3f, 0x55, 0xc5, 0xb8, 0x6a, 0x22, 0x49, 0xb5, + 0x0d, 0x49, 0xb5, 0x2c, 0xec, 0xaa, 0xae, 0x81, 0x2d, 0xe2, 0x43, 0xf0, 0x27, 0xab, 0xb8, 0x8a, + 0xe9, 0xbf, 0x92, 0xf7, 0x1f, 0x5b, 0xcd, 0xb3, 0x33, 0xf4, 0xd7, 0x56, 0xfd, 0x91, 0xe4, 0x1a, + 0x35, 0x44, 0x5c, 0xb5, 0x66, 0xb3, 0x0d, 0x85, 0x2c, 0xae, 0x86, 0x5e, 0xf8, 0x67, 0x66, 0xf7, + 0x3b, 0xd3, 0x98, 0x93, 0xc8, 0xb6, 0xea, 0x20, 0x5d, 0xd1, 0xb0, 0x45, 0xea, 0xb5, 0xf0, 0xc4, + 0x37, 0x3a, 0x9c, 0x78, 0x62, 0x38, 0x88, 0x6d, 0x9b, 0x72, 0x91, 0xa5, 0x23, 0xa7, 0x66, 0x58, + 0xae, 0xa4, 0x39, 0x4d, 0xdb, 0xc5, 0xd2, 0x63, 0xd4, 0x0c, 0x18, 0x9e, 0xd2, 0x30, 0xa9, 0x61, + 0xa2, 0xf8, 0x24, 0xfd, 0x1f, 0xfe, 0x23, 0xe1, 0x12, 0x98, 0xbc, 0xeb, 0x85, 0x73, 0x89, 0x99, + 0x5d, 0x45, 0x16, 0x22, 0x06, 0x91, 0xd1, 0x4e, 0x1d, 0x11, 0x17, 0x9e, 0x02, 0xa3, 0xbe, 0x6d, + 0x43, 0x9f, 0xe0, 0xa6, 0xb9, 0x99, 0x31, 0xf9, 0x2b, 0xf4, 0x77, 0x59, 0x17, 0x9e, 0x81, 0xa9, + 0xf4, 0x93, 0xc4, 0xc6, 0x16, 0x41, 0xf0, 0x7b, 0xe0, 0x68, 0xd5, 0x5f, 0x52, 0x88, 0xab, 0xba, + 0x88, 0x9e, 0x1f, 0x2f, 0xcc, 0x8a, 0xfb, 0xbd, 0xb1, 0xc6, 0x9c, 0xd8, 0x82, 0x75, 0xcf, 0x3b, + 0x57, 0x1c, 0x7e, 0xb3, 0x9b, 0x1f, 0x90, 0x3f, 0xaa, 0xc6, 0xd6, 0x84, 0x29, 0xc0, 0x27, 0x8c, + 0x2f, 0x79, 0x70, 0x81, 0xd7, 0x82, 0xda, 0x42, 0x2a, 0x78, 0xca, 0x3c, 0x2b, 0x82, 0x11, 0x6a, + 0x9e, 0x4c, 0x70, 0xd3, 0x43, 0x33, 0xe3, 0x85, 0xb3, 0x62, 0x86, 0x24, 0x12, 0x29, 0x88, 0xcc, + 0x4e, 0x0a, 0x67, 0xc0, 0x37, 0xdb, 0x4d, 0xdc, 0x73, 0x55, 0xc7, 0xad, 0x38, 0xd8, 0xc6, 0x44, + 0x35, 0x43, 0x6f, 0x5e, 0x72, 0x60, 0xa6, 0xfb, 0x5e, 0xe6, 0xdb, 0xf7, 0xc1, 0x98, 0x1d, 0x2c, + 0xb2, 0x88, 0x5d, 0xcf, 0xe6, 0x1e, 0x03, 0x5f, 0xd4, 0x75, 0xc3, 0xcb, 0xee, 0x08, 0x3a, 0x02, + 0x14, 0x66, 0xc0, 0x17, 0x69, 0x9e, 0x60, 0xbb, 0xcd, 0xe9, 0x1f, 0x73, 0xe9, 0x04, 0x13, 0x5b, + 0xc3, 0x37, 0xdd, 0xe6, 0xf3, 0x7c, 0x4f, 0x3e, 0xcb, 0xa8, 0x86, 0x1b, 0xaa, 0x99, 0xea, 0xf2, + 0x06, 0x38, 0x42, 0x4d, 0x77, 0x48, 0x45, 0x38, 0x09, 0xc6, 0x34, 0xd3, 0x40, 0x96, 0xeb, 0x3d, + 0x1b, 0xa4, 0xcf, 0x46, 0xfd, 0x85, 0xb2, 0x0e, 0x4f, 0x80, 0x23, 0x2e, 0xb6, 0x95, 0x3b, 0x13, + 0x43, 0xd3, 0xdc, 0xcc, 0x51, 0x79, 0xd8, 0xc5, 0xf6, 0x1d, 0xe1, 0x27, 0x1c, 0xf8, 0x8c, 0xd2, + 0xdb, 0x54, 0x4d, 0x43, 0x57, 0x5d, 0xec, 0xc4, 0xe2, 0xe7, 0x74, 0xcf, 0x7e, 0x38, 0x0f, 0x8e, + 0x07, 0x4c, 0x14, 0x55, 0xd7, 0x1d, 0x44, 0x88, 0x6f, 0xb9, 0x08, 0x3f, 0xec, 0xe6, 0x8f, 0x35, + 0xd5, 0x9a, 0x79, 0x45, 0x60, 0x0f, 0x04, 0xf9, 0xe3, 0x60, 0xef, 0xa2, 0xbf, 0x72, 0x65, 0xf4, + 0xe5, 0xeb, 0xfc, 0xc0, 0x3f, 0x5e, 0xe7, 0x07, 0x84, 0x75, 0x20, 0x74, 0x72, 0x84, 0x85, 0xf8, + 0x0c, 0x38, 0x1e, 0x34, 0x86, 0xd0, 0x9c, 0xef, 0xd1, 0xc7, 0x5a, 0x6c, 0xbf, 0x67, 0xac, 0x9d, + 0x5a, 0x25, 0x66, 0x3c, 0x1b, 0xb5, 0x36, 0x5b, 0x1d, 0xa8, 0xb5, 0xd8, 0xef, 0x44, 0x2d, 0xe9, + 0x48, 0x44, 0xad, 0x2d, 0x92, 0x8c, 0x5a, 0x4b, 0xd4, 0x84, 0x49, 0x70, 0x8a, 0x02, 0x6e, 0x6c, + 0x3b, 0xd8, 0x75, 0x4d, 0x44, 0x7b, 0x41, 0x90, 0xb1, 0x7f, 0xe4, 0x58, 0x4f, 0x68, 0x79, 0xca, + 0xcc, 0xe4, 0xc1, 0x38, 0x31, 0x55, 0xb2, 0xad, 0xd4, 0x90, 0x8b, 0x1c, 0x6a, 0x61, 0x48, 0x06, + 0x74, 0xe9, 0xb6, 0xb7, 0x02, 0x0b, 0xe0, 0x6b, 0xb1, 0x0d, 0x8a, 0x6a, 0x9a, 0xf8, 0x89, 0x6a, + 0x69, 0x88, 0x72, 0x1f, 0x92, 0x4f, 0x44, 0x5b, 0x17, 0x83, 0x47, 0xf0, 0x07, 0x60, 0xc2, 0x42, + 0x4f, 0x5d, 0xc5, 0x41, 0xb6, 0x89, 0x2c, 0x83, 0x6c, 0x2b, 0x9a, 0x6a, 0xe9, 0x1e, 0x59, 0x44, + 0xd3, 0x6d, 0xbc, 0xc0, 0x8b, 0xfe, 0x3d, 0x22, 0x06, 0xf7, 0x88, 0xb8, 0x11, 0xdc, 0x23, 0xc5, + 0x51, 0xaf, 0xb1, 0xbd, 0xfa, 0x6b, 0x9e, 0x93, 0x3f, 0xf1, 0x50, 0xe4, 0x00, 0x64, 0x29, 0xc0, + 0x10, 0xce, 0x83, 0xb3, 0x94, 0x92, 0x8c, 0xaa, 0x06, 0x71, 0x91, 0x83, 0xf4, 0xa8, 0x64, 0x9e, + 0xa8, 0x8e, 0x5e, 0x42, 0x16, 0xae, 0x85, 0x35, 0xbb, 0x0c, 0xce, 0x65, 0xda, 0xcd, 0x22, 0xf2, + 0x09, 0x18, 0xd1, 0xe9, 0x0a, 0x6d, 0x83, 0x63, 0x32, 0xfb, 0x25, 0xe4, 0x58, 0x63, 0xf7, 0xcb, + 0x11, 0xe9, 0xb4, 0xfc, 0xca, 0xa5, 0xd0, 0xcc, 0x0b, 0x0e, 0x9c, 0xde, 0x67, 0x03, 0x43, 0x7e, + 0x08, 0x8e, 0xd9, 0xf1, 0x67, 0x41, 0xa3, 0x2d, 0x64, 0xea, 0x0a, 0x09, 0x58, 0xd6, 0xfd, 0x5b, + 0xf0, 0x84, 0x32, 0x38, 0x9a, 0xd8, 0x06, 0x27, 0x00, 0xcb, 0xdf, 0x52, 0x32, 0x9d, 0x4b, 0x30, + 0x07, 0x40, 0xd0, 0x4d, 0xca, 0x25, 0xfa, 0x32, 0x87, 0xe5, 0xd8, 0x8a, 0x70, 0x0b, 0x48, 0x94, + 0xcd, 0xa2, 0x69, 0x56, 0x54, 0xc3, 0x21, 0x9b, 0xaa, 0xb9, 0x84, 0x2d, 0x2f, 0xe5, 0x8a, 0xc9, + 0xe6, 0x57, 0x2e, 0x65, 0xb8, 0x15, 0x7f, 0xc5, 0x81, 0xd9, 0xec, 0x70, 0x2c, 0x5e, 0x3b, 0xe0, + 0xab, 0xb6, 0x6a, 0x38, 0x4a, 0x43, 0x35, 0xbd, 0xfb, 0x9f, 0x96, 0x01, 0x0b, 0xd9, 0x4a, 0xb6, + 0x90, 0xa9, 0x86, 0x13, 0x19, 0x0a, 0xcb, 0xcc, 0x8a, 0x12, 0xe0, 0x98, 0x9d, 0xd8, 0x22, 0xfc, + 0x9b, 0x03, 0x9f, 0x75, 0x3d, 0x05, 0x57, 0xf6, 0xab, 0xcd, 0xe2, 0xe4, 0x87, 0xdd, 0xfc, 0xa7, + 0x7e, 0x2b, 0x68, 0xdd, 0xd1, 0xde, 0xee, 0x3c, 0x9c, 0x7d, 0x5a, 0x4a, 0x0c, 0xa7, 0x75, 0x47, + 0x7b, 0x6f, 0x81, 0x0b, 0xe0, 0xa3, 0x70, 0xd7, 0x63, 0xd4, 0x64, 0x35, 0x36, 0x25, 0x46, 0xd3, + 0x8f, 0xe8, 0x4f, 0x3f, 0x62, 0xa5, 0xbe, 0x65, 0x1a, 0xda, 0x1a, 0x6a, 0xca, 0xe3, 0xc1, 0x89, + 0x35, 0xd4, 0x14, 0x4e, 0x02, 0xe8, 0xa7, 0xae, 0xea, 0xa8, 0x51, 0xe1, 0x3c, 0x04, 0x27, 0x12, + 0xab, 0xec, 0xb5, 0x94, 0xc1, 0x88, 0x4d, 0x57, 0xd8, 0xa5, 0x76, 0x2e, 0xe3, 0xbb, 0xf0, 0x8e, + 0xb0, 0xbc, 0x65, 0x00, 0xc2, 0x2a, 0x2b, 0xe4, 0x44, 0x06, 0xac, 0xdb, 0x2e, 0xd2, 0xcb, 0x56, + 0xd8, 0x1e, 0xb3, 0x4c, 0x5d, 0x3b, 0xac, 0xc6, 0xbb, 0x01, 0x85, 0xa3, 0xce, 0xe9, 0x46, 0xb8, + 0xaa, 0xb4, 0xbe, 0x29, 0x14, 0x94, 0xfe, 0x64, 0xb4, 0xa9, 0x92, 0x7c, 0x75, 0x88, 0x08, 0x3b, + 0x2c, 0xa3, 0x93, 0xd3, 0x54, 0x68, 0xec, 0xa6, 0x4a, 0x36, 0x30, 0xfb, 0x15, 0x34, 0xe3, 0xd4, + 0xeb, 0x91, 0xcb, 0x7c, 0x3d, 0x0a, 0x2a, 0x98, 0xeb, 0xc1, 0x24, 0xe3, 0x7a, 0x1e, 0xc0, 0x30, + 0x39, 0x82, 0xf0, 0x05, 0x04, 0xc3, 0xf4, 0xf3, 0x4b, 0x4f, 0xa7, 0xd7, 0xe4, 0xb9, 0xf4, 0x8b, + 0x77, 0x09, 0xd7, 0x6a, 0x06, 0x21, 0x06, 0xb6, 0xe4, 0x18, 0xa3, 0xff, 0xdb, 0x2c, 0x20, 0xfc, + 0x90, 0x03, 0xe7, 0xb3, 0x79, 0xc2, 0x88, 0x56, 0xc0, 0xb0, 0x13, 0x0c, 0xd4, 0x63, 0xc5, 0x6b, + 0x5e, 0xa2, 0xfd, 0x65, 0x37, 0xff, 0x45, 0xd5, 0x70, 0xb7, 0xeb, 0x5b, 0xa2, 0x86, 0x6b, 0x6c, + 0xc4, 0x67, 0x7f, 0x2e, 0x10, 0xfd, 0xb1, 0xe4, 0x36, 0x6d, 0x44, 0xc4, 0x12, 0xd2, 0xfe, 0xf4, + 0xdb, 0x0b, 0x80, 0x29, 0x80, 0x12, 0xd2, 0x64, 0x8a, 0x24, 0xcc, 0x83, 0x69, 0xea, 0xc1, 0xba, + 0xa9, 0x23, 0xe2, 0xde, 0xb7, 0x34, 0x6c, 0x3d, 0x32, 0x9c, 0x1a, 0xd2, 0x37, 0x89, 0x96, 0x21, + 0x29, 0x7f, 0x1a, 0x8c, 0x1c, 0xe9, 0xe7, 0x99, 0xdb, 0x06, 0x80, 0x0d, 0xa2, 0x29, 0x04, 0x59, + 0xba, 0x12, 0x8a, 0x29, 0x56, 0x5a, 0x5f, 0x66, 0x2a, 0xad, 0x4d, 0xa2, 0xdd, 0x43, 0x96, 0x1e, + 0xdd, 0xa0, 0x7e, 0x91, 0x1d, 0x6f, 0xb4, 0xac, 0x17, 0x7e, 0x7f, 0x1a, 0x1c, 0xa1, 0x0e, 0xc1, + 0x3d, 0x0e, 0x9c, 0x4c, 0x93, 0x29, 0xf0, 0x46, 0x26, 0x8b, 0x1d, 0xb4, 0x11, 0xbf, 0x78, 0x00, + 0x04, 0x3f, 0x24, 0xc2, 0xf2, 0x8f, 0xde, 0xfd, 0xfd, 0xe7, 0x83, 0x0b, 0x70, 0xbe, 0xbb, 0xee, + 0x0d, 0x53, 0x9b, 0xe9, 0x20, 0xe9, 0x59, 0xf0, 0x36, 0x9e, 0xc3, 0x77, 0x1c, 0x6b, 0x60, 0xc9, + 0x7a, 0x81, 0x0b, 0xbd, 0x7b, 0x98, 0x10, 0x52, 0xfc, 0x8d, 0xfe, 0x01, 0x18, 0xc3, 0xcb, 0x94, + 0xe1, 0x45, 0x38, 0xd7, 0x03, 0x43, 0x5f, 0x62, 0xc1, 0x17, 0x83, 0x60, 0x62, 0x1f, 0xdd, 0x44, + 0xe0, 0xad, 0x3e, 0x3d, 0x4b, 0x95, 0x68, 0xfc, 0xed, 0x43, 0x42, 0x63, 0xa4, 0x6f, 0x52, 0xd2, + 0x45, 0x78, 0xa3, 0x57, 0xd2, 0x9e, 0x52, 0x76, 0x5c, 0x25, 0x54, 0x3f, 0xf0, 0xbf, 0x1c, 0xf8, + 0x34, 0x5d, 0x86, 0x11, 0xb8, 0xd6, 0xb7, 0xd3, 0xed, 0x7a, 0x8f, 0xbf, 0x75, 0x38, 0x60, 0x2c, + 0x00, 0xab, 0x34, 0x00, 0x8b, 0x70, 0xa1, 0x8f, 0x00, 0x60, 0x3b, 0xc6, 0xff, 0x5f, 0xc1, 0x50, + 0x9f, 0x2a, 0x8f, 0xe0, 0x4a, 0x76, 0xaf, 0x3b, 0x09, 0x3d, 0x7e, 0xf5, 0xc0, 0x38, 0x8c, 0xf8, + 0x22, 0x25, 0x7e, 0x15, 0x5e, 0xce, 0xf0, 0x21, 0x2b, 0x00, 0x52, 0x12, 0x83, 0x4f, 0x0a, 0xe5, + 0xf8, 0x95, 0xdc, 0x17, 0xe5, 0x14, 0x01, 0xd8, 0x17, 0xe5, 0x34, 0xfd, 0xd6, 0x1f, 0xe5, 0xc4, + 0x7d, 0x09, 0xff, 0xc0, 0xb1, 0xb1, 0x2c, 0x21, 0xdd, 0xe0, 0xf5, 0xec, 0x2e, 0xa6, 0x29, 0x42, + 0x7e, 0xa1, 0xef, 0xf3, 0x8c, 0xda, 0x25, 0x4a, 0xad, 0x00, 0x67, 0xbb, 0x53, 0x73, 0x19, 0x80, + 0xff, 0xad, 0x0b, 0xfe, 0x72, 0x10, 0x7c, 0x9e, 0x41, 0x8b, 0xc1, 0xf5, 0xec, 0x2e, 0x66, 0xd2, + 0x80, 0x7c, 0xe5, 0xf0, 0x00, 0x59, 0x10, 0xd6, 0x68, 0x10, 0x96, 0xe1, 0x52, 0xf7, 0x20, 0x38, + 0x21, 0x62, 0x94, 0xd3, 0x0e, 0xc5, 0x54, 0x7c, 0x6d, 0x09, 0xff, 0xd9, 0xa6, 0x1d, 0x93, 0x92, + 0x88, 0xc0, 0x1e, 0x6e, 0xd5, 0x7d, 0x04, 0x2a, 0x5f, 0x3c, 0x08, 0x04, 0x63, 0x5d, 0xa4, 0xac, + 0xaf, 0xc1, 0x2b, 0xdd, 0x59, 0x07, 0xd2, 0x54, 0x69, 0xbd, 0xc0, 0x7e, 0x31, 0xc8, 0x3e, 0xfc, + 0x65, 0xd0, 0x82, 0x70, 0x23, 0xbb, 0xd3, 0xd9, 0x95, 0x2a, 0x7f, 0xff, 0x90, 0x51, 0x59, 0x74, + 0xae, 0xd2, 0xe8, 0x7c, 0x09, 0x2f, 0xf6, 0xdc, 0xdf, 0x0d, 0x1d, 0xfe, 0x86, 0x03, 0xe3, 0x31, + 0xb9, 0x05, 0xbf, 0xdd, 0xc3, 0xeb, 0x8a, 0xcb, 0x36, 0xfe, 0x52, 0xef, 0x07, 0x99, 0xff, 0xb3, + 0xd4, 0xff, 0xb3, 0x70, 0x26, 0xc3, 0xdb, 0xf5, 0x9d, 0xfc, 0x59, 0x50, 0xd0, 0x9d, 0x85, 0x57, + 0x2f, 0x05, 0x9d, 0x49, 0x0b, 0xf6, 0x52, 0xd0, 0xd9, 0x34, 0x61, 0x2f, 0xd3, 0x09, 0xf6, 0x40, + 0x14, 0xc3, 0x52, 0x22, 0x7d, 0x18, 0x9f, 0x3b, 0x7f, 0x37, 0x08, 0xce, 0x64, 0xd6, 0x69, 0xf0, + 0x7e, 0xbf, 0xc3, 0x64, 0x47, 0xa9, 0xc9, 0x6f, 0x1e, 0x36, 0x2c, 0x0b, 0xd3, 0x03, 0x1a, 0xa6, + 0x0d, 0x28, 0xf7, 0x3c, 0xb9, 0x2a, 0x36, 0x72, 0xa2, 0x88, 0x49, 0xcf, 0x5a, 0xc5, 0xe1, 0x73, + 0xf8, 0xeb, 0x41, 0xf0, 0xf5, 0x2c, 0x92, 0x0f, 0x56, 0x0e, 0x30, 0x98, 0xa4, 0xea, 0x58, 0xfe, + 0xee, 0x21, 0x22, 0xb2, 0x48, 0x3d, 0xa4, 0x91, 0x7a, 0x00, 0xbf, 0xdb, 0x4b, 0xa4, 0x42, 0x28, + 0xc5, 0x53, 0xa0, 0xb1, 0xac, 0x4a, 0x8b, 0xd7, 0x7f, 0x38, 0xf6, 0xe5, 0x37, 0x4d, 0x60, 0xc2, + 0xe5, 0xec, 0x94, 0x3a, 0x08, 0x5c, 0x7e, 0xe5, 0xa0, 0x30, 0xbd, 0x5f, 0x98, 0x98, 0xe2, 0x28, + 0xf5, 0x08, 0x48, 0x69, 0x10, 0x2d, 0x16, 0x8c, 0xe2, 0x77, 0xde, 0xec, 0xe5, 0xb8, 0xb7, 0x7b, + 0x39, 0xee, 0x6f, 0x7b, 0x39, 0xee, 0xd5, 0xfb, 0xdc, 0xc0, 0xdb, 0xf7, 0xb9, 0x81, 0x3f, 0xbf, + 0xcf, 0x0d, 0x3c, 0x98, 0x6f, 0xd7, 0xfb, 0x91, 0xbd, 0x0b, 0xa1, 0xbd, 0xc6, 0xb7, 0xa4, 0xa7, + 0x2d, 0xa3, 0x4a, 0xd3, 0x46, 0x64, 0x6b, 0x84, 0x7e, 0x90, 0xbe, 0xf8, 0xbf, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x21, 0x1e, 0x15, 0x5a, 0x72, 0x1d, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1415,6 +1746,15 @@ type QueryClient interface { QueryAllPairsValConAddrByConsumerChainID(ctx context.Context, in *QueryAllPairsValConAddrByConsumerChainIDRequest, opts ...grpc.CallOption) (*QueryAllPairsValConAddrByConsumerChainIDResponse, error) // QueryParams returns all current values of provider parameters QueryParams(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // QueryConsumerChainOptedInValidators returns a list of validators consensus addresses + // that opted-in to the given consumer chain + QueryConsumerChainOptedInValidators(ctx context.Context, in *QueryConsumerChainOptedInValidatorsRequest, opts ...grpc.CallOption) (*QueryConsumerChainOptedInValidatorsResponse, error) + // QueryConsumerChainsValidatorHasToValidate returns a list of consumer chains + // that a given validator must validate + QueryConsumerChainsValidatorHasToValidate(ctx context.Context, in *QueryConsumerChainsValidatorHasToValidateRequest, opts ...grpc.CallOption) (*QueryConsumerChainsValidatorHasToValidateResponse, error) + // QueryValidatorConsumerCommissionRate returns the commission rate a given + // validator charges on a given consumer chain + QueryValidatorConsumerCommissionRate(ctx context.Context, in *QueryValidatorConsumerCommissionRateRequest, opts ...grpc.CallOption) (*QueryValidatorConsumerCommissionRateResponse, error) // QueryOldestUnconfirmedVsc returns the send timestamp of the oldest unconfirmed VSCPacket for a given chainID QueryOldestUnconfirmedVsc(ctx context.Context, in *QueryOldestUnconfirmedVscRequest, opts ...grpc.CallOption) (*QueryOldestUnconfirmedVscResponse, error) } @@ -1526,6 +1866,33 @@ func (c *queryClient) QueryParams(ctx context.Context, in *QueryParamsRequest, o return out, nil } +func (c *queryClient) QueryConsumerChainOptedInValidators(ctx context.Context, in *QueryConsumerChainOptedInValidatorsRequest, opts ...grpc.CallOption) (*QueryConsumerChainOptedInValidatorsResponse, error) { + out := new(QueryConsumerChainOptedInValidatorsResponse) + err := c.cc.Invoke(ctx, "/interchain_security.ccv.provider.v1.Query/QueryConsumerChainOptedInValidators", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) QueryConsumerChainsValidatorHasToValidate(ctx context.Context, in *QueryConsumerChainsValidatorHasToValidateRequest, opts ...grpc.CallOption) (*QueryConsumerChainsValidatorHasToValidateResponse, error) { + out := new(QueryConsumerChainsValidatorHasToValidateResponse) + err := c.cc.Invoke(ctx, "/interchain_security.ccv.provider.v1.Query/QueryConsumerChainsValidatorHasToValidate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) QueryValidatorConsumerCommissionRate(ctx context.Context, in *QueryValidatorConsumerCommissionRateRequest, opts ...grpc.CallOption) (*QueryValidatorConsumerCommissionRateResponse, error) { + out := new(QueryValidatorConsumerCommissionRateResponse) + err := c.cc.Invoke(ctx, "/interchain_security.ccv.provider.v1.Query/QueryValidatorConsumerCommissionRate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *queryClient) QueryOldestUnconfirmedVsc(ctx context.Context, in *QueryOldestUnconfirmedVscRequest, opts ...grpc.CallOption) (*QueryOldestUnconfirmedVscResponse, error) { out := new(QueryOldestUnconfirmedVscResponse) err := c.cc.Invoke(ctx, "/interchain_security.ccv.provider.v1.Query/QueryOldestUnconfirmedVsc", in, out, opts...) @@ -1567,6 +1934,15 @@ type QueryServer interface { QueryAllPairsValConAddrByConsumerChainID(context.Context, *QueryAllPairsValConAddrByConsumerChainIDRequest) (*QueryAllPairsValConAddrByConsumerChainIDResponse, error) // QueryParams returns all current values of provider parameters QueryParams(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // QueryConsumerChainOptedInValidators returns a list of validators consensus addresses + // that opted-in to the given consumer chain + QueryConsumerChainOptedInValidators(context.Context, *QueryConsumerChainOptedInValidatorsRequest) (*QueryConsumerChainOptedInValidatorsResponse, error) + // QueryConsumerChainsValidatorHasToValidate returns a list of consumer chains + // that a given validator must validate + QueryConsumerChainsValidatorHasToValidate(context.Context, *QueryConsumerChainsValidatorHasToValidateRequest) (*QueryConsumerChainsValidatorHasToValidateResponse, error) + // QueryValidatorConsumerCommissionRate returns the commission rate a given + // validator charges on a given consumer chain + QueryValidatorConsumerCommissionRate(context.Context, *QueryValidatorConsumerCommissionRateRequest) (*QueryValidatorConsumerCommissionRateResponse, error) // QueryOldestUnconfirmedVsc returns the send timestamp of the oldest unconfirmed VSCPacket for a given chainID QueryOldestUnconfirmedVsc(context.Context, *QueryOldestUnconfirmedVscRequest) (*QueryOldestUnconfirmedVscResponse, error) } @@ -1608,6 +1984,15 @@ func (*UnimplementedQueryServer) QueryAllPairsValConAddrByConsumerChainID(ctx co func (*UnimplementedQueryServer) QueryParams(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryParams not implemented") } +func (*UnimplementedQueryServer) QueryConsumerChainOptedInValidators(ctx context.Context, req *QueryConsumerChainOptedInValidatorsRequest) (*QueryConsumerChainOptedInValidatorsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryConsumerChainOptedInValidators not implemented") +} +func (*UnimplementedQueryServer) QueryConsumerChainsValidatorHasToValidate(ctx context.Context, req *QueryConsumerChainsValidatorHasToValidateRequest) (*QueryConsumerChainsValidatorHasToValidateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryConsumerChainsValidatorHasToValidate not implemented") +} +func (*UnimplementedQueryServer) QueryValidatorConsumerCommissionRate(ctx context.Context, req *QueryValidatorConsumerCommissionRateRequest) (*QueryValidatorConsumerCommissionRateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryValidatorConsumerCommissionRate not implemented") +} func (*UnimplementedQueryServer) QueryOldestUnconfirmedVsc(ctx context.Context, req *QueryOldestUnconfirmedVscRequest) (*QueryOldestUnconfirmedVscResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryOldestUnconfirmedVsc not implemented") } @@ -1814,6 +2199,60 @@ func _Query_QueryParams_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } +func _Query_QueryConsumerChainOptedInValidators_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryConsumerChainOptedInValidatorsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryConsumerChainOptedInValidators(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/interchain_security.ccv.provider.v1.Query/QueryConsumerChainOptedInValidators", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryConsumerChainOptedInValidators(ctx, req.(*QueryConsumerChainOptedInValidatorsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_QueryConsumerChainsValidatorHasToValidate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryConsumerChainsValidatorHasToValidateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryConsumerChainsValidatorHasToValidate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/interchain_security.ccv.provider.v1.Query/QueryConsumerChainsValidatorHasToValidate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryConsumerChainsValidatorHasToValidate(ctx, req.(*QueryConsumerChainsValidatorHasToValidateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_QueryValidatorConsumerCommissionRate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryValidatorConsumerCommissionRateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryValidatorConsumerCommissionRate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/interchain_security.ccv.provider.v1.Query/QueryValidatorConsumerCommissionRate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryValidatorConsumerCommissionRate(ctx, req.(*QueryValidatorConsumerCommissionRateRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Query_QueryOldestUnconfirmedVsc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryOldestUnconfirmedVscRequest) if err := dec(in); err != nil { @@ -1881,8 +2320,20 @@ var _Query_serviceDesc = grpc.ServiceDesc{ Handler: _Query_QueryParams_Handler, }, { - MethodName: "QueryOldestUnconfirmedVsc", - Handler: _Query_QueryOldestUnconfirmedVsc_Handler, + MethodName: "QueryConsumerChainOptedInValidators", + Handler: _Query_QueryConsumerChainOptedInValidators_Handler, + }, + { + MethodName: "QueryConsumerChainsValidatorHasToValidate", + Handler: _Query_QueryConsumerChainsValidatorHasToValidate_Handler, + }, + { + MethodName: "QueryValidatorConsumerCommissionRate", + Handler: _Query_QueryValidatorConsumerCommissionRate_Handler, + }, + { + MethodName: "QueryOldestUnconfirmedVsc", + Handler: _Query_QueryOldestUnconfirmedVsc_Handler, }, }, Streams: []grpc.StreamDesc{}, @@ -2148,6 +2599,11 @@ func (m *Chain) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Top_N != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Top_N)) + i-- + dAtA[i] = 0x18 + } if len(m.ClientId) > 0 { i -= len(m.ClientId) copy(dAtA[i:], m.ClientId) @@ -2685,6 +3141,200 @@ func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *QueryConsumerChainOptedInValidatorsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryConsumerChainOptedInValidatorsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsumerChainOptedInValidatorsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryConsumerChainOptedInValidatorsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryConsumerChainOptedInValidatorsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsumerChainOptedInValidatorsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ValidatorsProviderAddresses) > 0 { + for iNdEx := len(m.ValidatorsProviderAddresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ValidatorsProviderAddresses[iNdEx]) + copy(dAtA[i:], m.ValidatorsProviderAddresses[iNdEx]) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ValidatorsProviderAddresses[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryConsumerChainsValidatorHasToValidateRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryConsumerChainsValidatorHasToValidateRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsumerChainsValidatorHasToValidateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ProviderAddress) > 0 { + i -= len(m.ProviderAddress) + copy(dAtA[i:], m.ProviderAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ProviderAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryConsumerChainsValidatorHasToValidateResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryConsumerChainsValidatorHasToValidateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsumerChainsValidatorHasToValidateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ConsumerChainIds) > 0 { + for iNdEx := len(m.ConsumerChainIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ConsumerChainIds[iNdEx]) + copy(dAtA[i:], m.ConsumerChainIds[iNdEx]) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ConsumerChainIds[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryValidatorConsumerCommissionRateRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryValidatorConsumerCommissionRateRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorConsumerCommissionRateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ProviderAddress) > 0 { + i -= len(m.ProviderAddress) + copy(dAtA[i:], m.ProviderAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ProviderAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryValidatorConsumerCommissionRateResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryValidatorConsumerCommissionRateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorConsumerCommissionRateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Rate.Size() + i -= size + if _, err := m.Rate.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func (m *QueryOldestUnconfirmedVscRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -2865,6 +3515,9 @@ func (m *Chain) Size() (n int) { if l > 0 { n += 1 + l + sovQuery(uint64(l)) } + if m.Top_N != 0 { + n += 1 + sovQuery(uint64(m.Top_N)) + } return n } @@ -3087,6 +3740,90 @@ func (m *QueryParamsResponse) Size() (n int) { return n } +func (m *QueryConsumerChainOptedInValidatorsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryConsumerChainOptedInValidatorsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ValidatorsProviderAddresses) > 0 { + for _, s := range m.ValidatorsProviderAddresses { + l = len(s) + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryConsumerChainsValidatorHasToValidateRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ProviderAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryConsumerChainsValidatorHasToValidateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ConsumerChainIds) > 0 { + for _, s := range m.ConsumerChainIds { + l = len(s) + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryValidatorConsumerCommissionRateRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ProviderAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryValidatorConsumerCommissionRateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Rate.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + func (m *QueryOldestUnconfirmedVscRequest) Size() (n int) { if m == nil { return 0 @@ -3781,13 +4518,32 @@ func (m *Chain) Unmarshal(dAtA []byte) error { } m.ClientId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipQuery(dAtA[iNdEx:]) - if err != nil { - return err + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Top_N", wireType) } - if (skippy < 0) || (iNdEx+skippy) < 0 { + m.Top_N = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Top_N |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthQuery } if (iNdEx + skippy) > l { @@ -5181,6 +5937,532 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryConsumerChainOptedInValidatorsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryConsumerChainOptedInValidatorsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsumerChainOptedInValidatorsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryConsumerChainOptedInValidatorsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryConsumerChainOptedInValidatorsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsumerChainOptedInValidatorsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorsProviderAddresses", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorsProviderAddresses = append(m.ValidatorsProviderAddresses, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryConsumerChainsValidatorHasToValidateRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryConsumerChainsValidatorHasToValidateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsumerChainsValidatorHasToValidateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProviderAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProviderAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryConsumerChainsValidatorHasToValidateResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryConsumerChainsValidatorHasToValidateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsumerChainsValidatorHasToValidateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsumerChainIds", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConsumerChainIds = append(m.ConsumerChainIds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryValidatorConsumerCommissionRateRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryValidatorConsumerCommissionRateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorConsumerCommissionRateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProviderAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProviderAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryValidatorConsumerCommissionRateResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryValidatorConsumerCommissionRateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorConsumerCommissionRateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rate", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Rate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *QueryOldestUnconfirmedVscRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/ccv/provider/types/query.pb.gw.go b/x/ccv/provider/types/query.pb.gw.go index 0ebfbfa6fd..2be0974fbf 100644 --- a/x/ccv/provider/types/query.pb.gw.go +++ b/x/ccv/provider/types/query.pb.gw.go @@ -321,6 +321,190 @@ func local_request_Query_QueryParams_0(ctx context.Context, marshaler runtime.Ma } +func request_Query_QueryConsumerChainOptedInValidators_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConsumerChainOptedInValidatorsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["chain_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "chain_id") + } + + protoReq.ChainId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chain_id", err) + } + + msg, err := client.QueryConsumerChainOptedInValidators(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryConsumerChainOptedInValidators_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConsumerChainOptedInValidatorsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["chain_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "chain_id") + } + + protoReq.ChainId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chain_id", err) + } + + msg, err := server.QueryConsumerChainOptedInValidators(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_QueryConsumerChainsValidatorHasToValidate_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConsumerChainsValidatorHasToValidateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["provider_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "provider_address") + } + + protoReq.ProviderAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "provider_address", err) + } + + msg, err := client.QueryConsumerChainsValidatorHasToValidate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryConsumerChainsValidatorHasToValidate_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConsumerChainsValidatorHasToValidateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["provider_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "provider_address") + } + + protoReq.ProviderAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "provider_address", err) + } + + msg, err := server.QueryConsumerChainsValidatorHasToValidate(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_QueryValidatorConsumerCommissionRate_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorConsumerCommissionRateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["chain_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "chain_id") + } + + protoReq.ChainId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chain_id", err) + } + + val, ok = pathParams["provider_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "provider_address") + } + + protoReq.ProviderAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "provider_address", err) + } + + msg, err := client.QueryValidatorConsumerCommissionRate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryValidatorConsumerCommissionRate_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorConsumerCommissionRateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["chain_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "chain_id") + } + + protoReq.ChainId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chain_id", err) + } + + val, ok = pathParams["provider_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "provider_address") + } + + protoReq.ProviderAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "provider_address", err) + } + + msg, err := server.QueryValidatorConsumerCommissionRate(ctx, &protoReq) + return msg, metadata, err + +} + func request_Query_QueryOldestUnconfirmedVsc_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryOldestUnconfirmedVscRequest var metadata runtime.ServerMetadata @@ -634,6 +818,75 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_QueryConsumerChainOptedInValidators_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryConsumerChainOptedInValidators_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryConsumerChainOptedInValidators_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryConsumerChainsValidatorHasToValidate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryConsumerChainsValidatorHasToValidate_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryConsumerChainsValidatorHasToValidate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryValidatorConsumerCommissionRate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryValidatorConsumerCommissionRate_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryValidatorConsumerCommissionRate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_QueryOldestUnconfirmedVsc_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -918,6 +1171,66 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_QueryConsumerChainOptedInValidators_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryConsumerChainOptedInValidators_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryConsumerChainOptedInValidators_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryConsumerChainsValidatorHasToValidate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryConsumerChainsValidatorHasToValidate_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryConsumerChainsValidatorHasToValidate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_QueryValidatorConsumerCommissionRate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryValidatorConsumerCommissionRate_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryValidatorConsumerCommissionRate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_QueryOldestUnconfirmedVsc_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -964,6 +1277,12 @@ var ( pattern_Query_QueryParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"interchain_security", "ccv", "provider", "params"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_QueryConsumerChainOptedInValidators_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"interchain_security", "ccv", "provider", "opted_in_validators", "chain_id"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryConsumerChainsValidatorHasToValidate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"interchain_security", "ccv", "provider", "consumer_chains_per_validator", "provider_address"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryValidatorConsumerCommissionRate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"interchain_security", "ccv", "provider", "consumer_commission_rate", "chain_id", "provider_address"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_QueryOldestUnconfirmedVsc_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"interchain_security", "ccv", "provider", "oldest_unconfirmed_vsc", "chain_id"}, "", runtime.AssumeColonVerbOpt(false))) ) @@ -990,5 +1309,11 @@ var ( forward_Query_QueryParams_0 = runtime.ForwardResponseMessage + forward_Query_QueryConsumerChainOptedInValidators_0 = runtime.ForwardResponseMessage + + forward_Query_QueryConsumerChainsValidatorHasToValidate_0 = runtime.ForwardResponseMessage + + forward_Query_QueryValidatorConsumerCommissionRate_0 = runtime.ForwardResponseMessage + forward_Query_QueryOldestUnconfirmedVsc_0 = runtime.ForwardResponseMessage ) diff --git a/x/ccv/provider/types/tx.pb.go b/x/ccv/provider/types/tx.pb.go index f72e95acb2..1fd4869548 100644 --- a/x/ccv/provider/types/tx.pb.go +++ b/x/ccv/provider/types/tx.pb.go @@ -9,6 +9,7 @@ import ( types "github.com/cometbft/cometbft/proto/tendermint/types" _ "github.com/cosmos/cosmos-proto" _ "github.com/cosmos/cosmos-sdk/codec/types" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" proto "github.com/cosmos/gogoproto/proto" @@ -270,6 +271,243 @@ func (m *MsgSubmitConsumerDoubleVotingResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgSubmitConsumerDoubleVotingResponse proto.InternalMessageInfo +type MsgOptIn struct { + // the chain id of the consumer chain to opt in to + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + // the validator address on the provider + ProviderAddr string `protobuf:"bytes,2,opt,name=provider_addr,json=providerAddr,proto3" json:"provider_addr,omitempty" yaml:"address"` + // (optional) The consensus public key to use on the consumer in json string format corresponding to proto-any, + // for example `{"@type":"/cosmos.crypto.ed25519.PubKey","key":"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is="}`. + // This field is optional and can remain empty (i.e., `consumer_key = ""`). A validator can always change the + // consumer public key at a later stage by issuing a `MsgAssignConsumerKey` message. + ConsumerKey string `protobuf:"bytes,3,opt,name=consumer_key,json=consumerKey,proto3" json:"consumer_key,omitempty"` +} + +func (m *MsgOptIn) Reset() { *m = MsgOptIn{} } +func (m *MsgOptIn) String() string { return proto.CompactTextString(m) } +func (*MsgOptIn) ProtoMessage() {} +func (*MsgOptIn) Descriptor() ([]byte, []int) { + return fileDescriptor_43221a4391e9fbf4, []int{6} +} +func (m *MsgOptIn) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgOptIn) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgOptIn.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgOptIn) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgOptIn.Merge(m, src) +} +func (m *MsgOptIn) XXX_Size() int { + return m.Size() +} +func (m *MsgOptIn) XXX_DiscardUnknown() { + xxx_messageInfo_MsgOptIn.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgOptIn proto.InternalMessageInfo + +type MsgOptInResponse struct { +} + +func (m *MsgOptInResponse) Reset() { *m = MsgOptInResponse{} } +func (m *MsgOptInResponse) String() string { return proto.CompactTextString(m) } +func (*MsgOptInResponse) ProtoMessage() {} +func (*MsgOptInResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_43221a4391e9fbf4, []int{7} +} +func (m *MsgOptInResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgOptInResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgOptInResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgOptInResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgOptInResponse.Merge(m, src) +} +func (m *MsgOptInResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgOptInResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgOptInResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgOptInResponse proto.InternalMessageInfo + +type MsgOptOut struct { + // the chain id of the consumer chain to opt out from + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + // the validator address on the provider + ProviderAddr string `protobuf:"bytes,2,opt,name=provider_addr,json=providerAddr,proto3" json:"provider_addr,omitempty" yaml:"address"` +} + +func (m *MsgOptOut) Reset() { *m = MsgOptOut{} } +func (m *MsgOptOut) String() string { return proto.CompactTextString(m) } +func (*MsgOptOut) ProtoMessage() {} +func (*MsgOptOut) Descriptor() ([]byte, []int) { + return fileDescriptor_43221a4391e9fbf4, []int{8} +} +func (m *MsgOptOut) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgOptOut) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgOptOut.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgOptOut) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgOptOut.Merge(m, src) +} +func (m *MsgOptOut) XXX_Size() int { + return m.Size() +} +func (m *MsgOptOut) XXX_DiscardUnknown() { + xxx_messageInfo_MsgOptOut.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgOptOut proto.InternalMessageInfo + +type MsgOptOutResponse struct { +} + +func (m *MsgOptOutResponse) Reset() { *m = MsgOptOutResponse{} } +func (m *MsgOptOutResponse) String() string { return proto.CompactTextString(m) } +func (*MsgOptOutResponse) ProtoMessage() {} +func (*MsgOptOutResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_43221a4391e9fbf4, []int{9} +} +func (m *MsgOptOutResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgOptOutResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgOptOutResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgOptOutResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgOptOutResponse.Merge(m, src) +} +func (m *MsgOptOutResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgOptOutResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgOptOutResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgOptOutResponse proto.InternalMessageInfo + +// MsgSetConsumerCommissionRate allows validators to set +// a per-consumer chain commission rate +type MsgSetConsumerCommissionRate struct { + // The validator address on the provider + ProviderAddr string `protobuf:"bytes,1,opt,name=provider_addr,json=providerAddr,proto3" json:"provider_addr,omitempty" yaml:"address"` + // The chain id of the consumer chain to set a commission rate + ChainId string `protobuf:"bytes,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + // The rate to charge delegators on the consumer chain, as a fraction + Rate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,3,opt,name=rate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"rate"` +} + +func (m *MsgSetConsumerCommissionRate) Reset() { *m = MsgSetConsumerCommissionRate{} } +func (m *MsgSetConsumerCommissionRate) String() string { return proto.CompactTextString(m) } +func (*MsgSetConsumerCommissionRate) ProtoMessage() {} +func (*MsgSetConsumerCommissionRate) Descriptor() ([]byte, []int) { + return fileDescriptor_43221a4391e9fbf4, []int{10} +} +func (m *MsgSetConsumerCommissionRate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSetConsumerCommissionRate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSetConsumerCommissionRate.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSetConsumerCommissionRate) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSetConsumerCommissionRate.Merge(m, src) +} +func (m *MsgSetConsumerCommissionRate) XXX_Size() int { + return m.Size() +} +func (m *MsgSetConsumerCommissionRate) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSetConsumerCommissionRate.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSetConsumerCommissionRate proto.InternalMessageInfo + +type MsgSetConsumerCommissionRateResponse struct { +} + +func (m *MsgSetConsumerCommissionRateResponse) Reset() { *m = MsgSetConsumerCommissionRateResponse{} } +func (m *MsgSetConsumerCommissionRateResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSetConsumerCommissionRateResponse) ProtoMessage() {} +func (*MsgSetConsumerCommissionRateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_43221a4391e9fbf4, []int{11} +} +func (m *MsgSetConsumerCommissionRateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSetConsumerCommissionRateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSetConsumerCommissionRateResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSetConsumerCommissionRateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSetConsumerCommissionRateResponse.Merge(m, src) +} +func (m *MsgSetConsumerCommissionRateResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSetConsumerCommissionRateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSetConsumerCommissionRateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSetConsumerCommissionRateResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*MsgAssignConsumerKey)(nil), "interchain_security.ccv.provider.v1.MsgAssignConsumerKey") proto.RegisterType((*MsgAssignConsumerKeyResponse)(nil), "interchain_security.ccv.provider.v1.MsgAssignConsumerKeyResponse") @@ -277,6 +515,12 @@ func init() { proto.RegisterType((*MsgSubmitConsumerMisbehaviourResponse)(nil), "interchain_security.ccv.provider.v1.MsgSubmitConsumerMisbehaviourResponse") proto.RegisterType((*MsgSubmitConsumerDoubleVoting)(nil), "interchain_security.ccv.provider.v1.MsgSubmitConsumerDoubleVoting") proto.RegisterType((*MsgSubmitConsumerDoubleVotingResponse)(nil), "interchain_security.ccv.provider.v1.MsgSubmitConsumerDoubleVotingResponse") + proto.RegisterType((*MsgOptIn)(nil), "interchain_security.ccv.provider.v1.MsgOptIn") + proto.RegisterType((*MsgOptInResponse)(nil), "interchain_security.ccv.provider.v1.MsgOptInResponse") + proto.RegisterType((*MsgOptOut)(nil), "interchain_security.ccv.provider.v1.MsgOptOut") + proto.RegisterType((*MsgOptOutResponse)(nil), "interchain_security.ccv.provider.v1.MsgOptOutResponse") + proto.RegisterType((*MsgSetConsumerCommissionRate)(nil), "interchain_security.ccv.provider.v1.MsgSetConsumerCommissionRate") + proto.RegisterType((*MsgSetConsumerCommissionRateResponse)(nil), "interchain_security.ccv.provider.v1.MsgSetConsumerCommissionRateResponse") } func init() { @@ -284,46 +528,57 @@ func init() { } var fileDescriptor_43221a4391e9fbf4 = []byte{ - // 610 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xcf, 0x4f, 0xd4, 0x4e, - 0x14, 0xdf, 0x42, 0xf2, 0xfd, 0xc2, 0x80, 0x26, 0x36, 0x10, 0x60, 0x83, 0x5d, 0x5d, 0xa3, 0x78, - 0xc0, 0x99, 0x80, 0x26, 0x46, 0x12, 0x0f, 0xac, 0x98, 0xf8, 0x23, 0x9b, 0x98, 0x9a, 0x60, 0xe2, - 0xc1, 0xa6, 0x9d, 0x3e, 0xba, 0x13, 0xda, 0x99, 0xcd, 0xcc, 0xb4, 0xa1, 0xff, 0x01, 0x47, 0x3d, - 0x19, 0x6f, 0xfc, 0x01, 0xfe, 0x21, 0x1e, 0x39, 0x7a, 0x32, 0x06, 0x2e, 0x9e, 0xbd, 0x78, 0x35, - 0x3b, 0x6d, 0xd9, 0x12, 0x2b, 0x10, 0xbc, 0xf5, 0xbd, 0xf7, 0x79, 0xef, 0x7d, 0x3e, 0x6f, 0x5e, - 0x1f, 0x5a, 0x65, 0x5c, 0x83, 0xa4, 0x03, 0x9f, 0x71, 0x4f, 0x01, 0x4d, 0x25, 0xd3, 0x39, 0xa1, - 0x34, 0x23, 0x43, 0x29, 0x32, 0x16, 0x82, 0x24, 0xd9, 0x1a, 0xd1, 0x7b, 0x78, 0x28, 0x85, 0x16, - 0xf6, 0xad, 0x06, 0x34, 0xa6, 0x34, 0xc3, 0x15, 0x1a, 0x67, 0x6b, 0xed, 0xb9, 0x48, 0x44, 0xc2, - 0xe0, 0xc9, 0xe8, 0xab, 0x48, 0x6d, 0x2f, 0x51, 0xa1, 0x12, 0xa1, 0xbc, 0x22, 0x50, 0x18, 0x55, - 0x28, 0x12, 0x22, 0x8a, 0x81, 0x18, 0x2b, 0x48, 0x77, 0x88, 0xcf, 0xf3, 0x32, 0x44, 0x58, 0x40, - 0x49, 0xcc, 0xa2, 0x81, 0xa6, 0x31, 0x03, 0xae, 0x15, 0xd1, 0xc0, 0x43, 0x90, 0x09, 0xe3, 0xda, - 0x30, 0x3b, 0xb1, 0xca, 0x84, 0x4e, 0x2d, 0xae, 0xf3, 0x21, 0x28, 0x02, 0x23, 0x62, 0x9c, 0x42, - 0x01, 0xe8, 0x7e, 0xb4, 0xd0, 0x5c, 0x5f, 0x45, 0x9b, 0x4a, 0xb1, 0x88, 0x3f, 0x11, 0x5c, 0xa5, - 0x09, 0xc8, 0x97, 0x90, 0xdb, 0x4b, 0x68, 0xaa, 0x10, 0xc6, 0xc2, 0x45, 0xeb, 0x86, 0x75, 0x77, - 0xda, 0xfd, 0xdf, 0xd8, 0xcf, 0x43, 0xfb, 0x21, 0xba, 0x52, 0x09, 0xf4, 0xfc, 0x30, 0x94, 0x8b, - 0x13, 0xa3, 0x78, 0xcf, 0xfe, 0xf9, 0xad, 0x73, 0x35, 0xf7, 0x93, 0x78, 0xa3, 0x3b, 0xf2, 0x82, - 0x52, 0x5d, 0x77, 0xb6, 0x02, 0x6e, 0x86, 0xa1, 0xb4, 0x6f, 0xa2, 0x59, 0x5a, 0xb6, 0xf0, 0x76, - 0x21, 0x5f, 0x9c, 0x34, 0x75, 0x67, 0xe8, 0xb8, 0xed, 0xc6, 0xd4, 0xfe, 0x41, 0xa7, 0xf5, 0xe3, - 0xa0, 0xd3, 0xea, 0x3a, 0x68, 0xb9, 0x89, 0x98, 0x0b, 0x6a, 0x28, 0xb8, 0x82, 0xee, 0x27, 0x0b, - 0x5d, 0xef, 0xab, 0xe8, 0x75, 0x1a, 0x24, 0x4c, 0x57, 0x80, 0x3e, 0x53, 0x01, 0x0c, 0xfc, 0x8c, - 0x89, 0x54, 0xda, 0xcb, 0x68, 0x5a, 0x99, 0xa8, 0x06, 0x59, 0x6a, 0x18, 0x3b, 0xec, 0x57, 0x68, - 0x36, 0xa9, 0xa1, 0x8d, 0x88, 0x99, 0xf5, 0x55, 0xcc, 0x02, 0x8a, 0xeb, 0x23, 0xc6, 0xb5, 0xa1, - 0x66, 0x6b, 0xb8, 0xde, 0xc1, 0x3d, 0x55, 0xa1, 0xc6, 0x7d, 0x05, 0xdd, 0x3e, 0x93, 0xda, 0x89, - 0x88, 0xfd, 0x89, 0x06, 0x11, 0x5b, 0x22, 0x0d, 0x62, 0xd8, 0x16, 0x9a, 0xf1, 0xe8, 0x1c, 0x11, - 0x1e, 0x5a, 0x08, 0xd3, 0x61, 0xcc, 0xa8, 0xaf, 0xc1, 0xcb, 0x84, 0x06, 0xaf, 0x7a, 0xdf, 0x52, - 0xcf, 0x4a, 0x9d, 0xbe, 0xd9, 0x00, 0xbc, 0x55, 0x25, 0x6c, 0x0b, 0x0d, 0x4f, 0x4b, 0xb8, 0x3b, - 0x1f, 0x36, 0xb9, 0xed, 0x77, 0x68, 0x81, 0xf1, 0x1d, 0xe9, 0x53, 0xcd, 0x04, 0xf7, 0x82, 0x58, - 0xd0, 0x5d, 0x6f, 0x00, 0x7e, 0x08, 0xd2, 0xbc, 0xde, 0xcc, 0xfa, 0x9d, 0xf3, 0x06, 0xf6, 0xcc, - 0xa0, 0xdd, 0xf9, 0x71, 0x99, 0xde, 0xa8, 0x4a, 0xe1, 0x3e, 0x67, 0x66, 0xf5, 0x49, 0x54, 0x33, - 0x5b, 0xff, 0x35, 0x89, 0x26, 0xfb, 0x2a, 0xb2, 0x3f, 0x58, 0xe8, 0xda, 0x9f, 0x7b, 0xfb, 0x08, - 0x5f, 0xe0, 0xa7, 0xc4, 0x4d, 0x9b, 0xd5, 0xde, 0xbc, 0x74, 0x6a, 0xc5, 0xcd, 0xfe, 0x6c, 0xa1, - 0xf6, 0x19, 0x1b, 0xd9, 0xbb, 0x68, 0x87, 0xbf, 0xd7, 0x68, 0xbf, 0xf8, 0xf7, 0x1a, 0x67, 0xd0, - 0x3d, 0xb5, 0x7b, 0x97, 0xa4, 0x5b, 0xaf, 0x71, 0x59, 0xba, 0x4d, 0x2f, 0xdf, 0x7b, 0xf3, 0xe5, - 0xc8, 0xb1, 0x0e, 0x8f, 0x1c, 0xeb, 0xfb, 0x91, 0x63, 0xbd, 0x3f, 0x76, 0x5a, 0x87, 0xc7, 0x4e, - 0xeb, 0xeb, 0xb1, 0xd3, 0x7a, 0xfb, 0x38, 0x62, 0x7a, 0x90, 0x06, 0x98, 0x8a, 0xa4, 0x3c, 0xa6, - 0x64, 0xdc, 0xf6, 0xde, 0xc9, 0x25, 0xcf, 0x1e, 0x90, 0xbd, 0xd3, 0xe7, 0xdc, 0xfc, 0x12, 0xc1, - 0x7f, 0xe6, 0x18, 0xde, 0xff, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x74, 0xf3, 0x13, 0x1f, 0xff, 0x05, - 0x00, 0x00, + // 787 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x56, 0xcd, 0x4f, 0xd4, 0x4e, + 0x18, 0xde, 0xc2, 0xef, 0xc7, 0xc7, 0x80, 0x46, 0x2a, 0x04, 0x68, 0x70, 0x57, 0x57, 0x05, 0x0f, + 0x6c, 0x1b, 0xf0, 0x2b, 0x12, 0x3d, 0xb0, 0x60, 0x22, 0x9a, 0x0d, 0xa4, 0x24, 0x98, 0x78, 0xb0, + 0x69, 0xa7, 0x43, 0x77, 0xc2, 0x76, 0xa6, 0xe9, 0x4c, 0x1b, 0xf6, 0xee, 0x81, 0xc4, 0x8b, 0x9e, + 0x8c, 0x37, 0xae, 0x26, 0x1e, 0xfd, 0x23, 0x38, 0x19, 0xe2, 0xc9, 0x78, 0x20, 0x06, 0x2e, 0x9e, + 0xfd, 0x0b, 0xcc, 0x4e, 0x3f, 0xb6, 0x1b, 0x96, 0x65, 0x81, 0x18, 0x4f, 0xbb, 0x33, 0xef, 0x33, + 0xcf, 0xfb, 0x3c, 0x6f, 0xa7, 0x4f, 0x0a, 0x66, 0x31, 0xe1, 0xc8, 0x87, 0x55, 0x13, 0x13, 0x83, + 0x21, 0x18, 0xf8, 0x98, 0xd7, 0x35, 0x08, 0x43, 0xcd, 0xf3, 0x69, 0x88, 0x6d, 0xe4, 0x6b, 0xe1, + 0x9c, 0xc6, 0xb7, 0x55, 0xcf, 0xa7, 0x9c, 0xca, 0x37, 0xdb, 0xa0, 0x55, 0x08, 0x43, 0x35, 0x41, + 0xab, 0xe1, 0x9c, 0x32, 0xea, 0x50, 0x87, 0x0a, 0xbc, 0xd6, 0xf8, 0x17, 0x1d, 0x55, 0x26, 0x21, + 0x65, 0x2e, 0x65, 0x46, 0x54, 0x88, 0x16, 0x49, 0xc9, 0xa1, 0xd4, 0xa9, 0x21, 0x4d, 0xac, 0xac, + 0x60, 0x53, 0x33, 0x49, 0x3d, 0x2e, 0x69, 0xd8, 0x82, 0x5a, 0x0d, 0x3b, 0x55, 0x0e, 0x6b, 0x18, + 0x11, 0xce, 0x34, 0x8e, 0x88, 0x8d, 0x7c, 0x17, 0x13, 0x2e, 0x94, 0xa5, 0xab, 0xf8, 0x40, 0x21, + 0x53, 0xe7, 0x75, 0x0f, 0x31, 0x0d, 0x35, 0x84, 0x11, 0x88, 0x22, 0x40, 0xf1, 0x83, 0x04, 0x46, + 0x2b, 0xcc, 0x59, 0x64, 0x0c, 0x3b, 0x64, 0x89, 0x12, 0x16, 0xb8, 0xc8, 0x7f, 0x81, 0xea, 0xf2, + 0x24, 0x18, 0x88, 0x8c, 0x61, 0x7b, 0x42, 0xba, 0x2e, 0xdd, 0x19, 0xd4, 0xfb, 0xc5, 0x7a, 0xc5, + 0x96, 0x1f, 0x82, 0x4b, 0x89, 0x41, 0xc3, 0xb4, 0x6d, 0x7f, 0xa2, 0xa7, 0x51, 0x2f, 0xcb, 0xbf, + 0x0f, 0x0a, 0x97, 0xeb, 0xa6, 0x5b, 0x5b, 0x28, 0x36, 0x76, 0x11, 0x63, 0x45, 0x7d, 0x38, 0x01, + 0x2e, 0xda, 0xb6, 0x2f, 0xdf, 0x00, 0xc3, 0x30, 0x6e, 0x61, 0x6c, 0xa1, 0xfa, 0x44, 0xaf, 0xe0, + 0x1d, 0x82, 0xcd, 0xb6, 0x0b, 0x03, 0x3b, 0xbb, 0x85, 0xdc, 0xaf, 0xdd, 0x42, 0xae, 0x98, 0x07, + 0x53, 0xed, 0x84, 0xe9, 0x88, 0x79, 0x94, 0x30, 0x54, 0xfc, 0x28, 0x81, 0x6b, 0x15, 0xe6, 0xac, + 0x07, 0x96, 0x8b, 0x79, 0x02, 0xa8, 0x60, 0x66, 0xa1, 0xaa, 0x19, 0x62, 0x1a, 0xf8, 0xf2, 0x14, + 0x18, 0x64, 0xa2, 0xca, 0x91, 0x1f, 0x7b, 0x68, 0x6e, 0xc8, 0x6b, 0x60, 0xd8, 0xcd, 0xa0, 0x85, + 0x89, 0xa1, 0xf9, 0x59, 0x15, 0x5b, 0x50, 0xcd, 0x8e, 0x58, 0xcd, 0x0c, 0x35, 0x9c, 0x53, 0xb3, + 0x1d, 0xf4, 0x16, 0x86, 0x8c, 0xf6, 0x19, 0x70, 0xbb, 0xa3, 0xb4, 0xd4, 0xc4, 0x4e, 0x4f, 0x1b, + 0x13, 0xcb, 0x34, 0xb0, 0x6a, 0x68, 0x83, 0x72, 0x4c, 0x9c, 0x53, 0x4c, 0x18, 0x60, 0xdc, 0x0e, + 0xbc, 0x1a, 0x86, 0x26, 0x47, 0x46, 0x48, 0x39, 0x32, 0x92, 0xe7, 0x1b, 0xfb, 0x99, 0xc9, 0xca, + 0x17, 0x37, 0x40, 0x5d, 0x4e, 0x0e, 0x6c, 0x50, 0x8e, 0x9e, 0xc6, 0x70, 0x7d, 0xcc, 0x6e, 0xb7, + 0x2d, 0xbf, 0x06, 0xe3, 0x98, 0x6c, 0xfa, 0x26, 0xe4, 0x98, 0x12, 0xc3, 0xaa, 0x51, 0xb8, 0x65, + 0x54, 0x91, 0x69, 0x23, 0x5f, 0x3c, 0xbd, 0xa1, 0xf9, 0xe9, 0xd3, 0x06, 0xf6, 0x4c, 0xa0, 0xf5, + 0xb1, 0x26, 0x4d, 0xb9, 0xc1, 0x12, 0x6d, 0x9f, 0x32, 0xb3, 0xec, 0x24, 0xd2, 0x99, 0xbd, 0x95, + 0xc0, 0x40, 0x85, 0x39, 0xab, 0x1e, 0x5f, 0x21, 0xff, 0xfe, 0x9a, 0xca, 0xe0, 0x4a, 0x22, 0x26, + 0x55, 0x88, 0xc1, 0x60, 0xb4, 0xb7, 0x1a, 0xf0, 0xbf, 0xa1, 0x30, 0xd3, 0xfe, 0x2a, 0x18, 0x49, + 0x5b, 0xa5, 0xfd, 0xbf, 0x4a, 0xe2, 0xdd, 0x59, 0x47, 0xe9, 0x20, 0x97, 0xa8, 0xeb, 0x62, 0xc6, + 0x30, 0x25, 0xba, 0xc9, 0xd1, 0xf1, 0xc6, 0x52, 0x97, 0xa3, 0xc9, 0x9a, 0xe9, 0x69, 0x35, 0xb3, + 0x06, 0xfe, 0xf3, 0x4d, 0x8e, 0xa2, 0x69, 0x95, 0x1f, 0xef, 0x1d, 0x14, 0x72, 0x3f, 0x0e, 0x0a, + 0xd3, 0x0e, 0xe6, 0xd5, 0xc0, 0x52, 0x21, 0x75, 0xe3, 0x94, 0x8b, 0x7f, 0x4a, 0xcc, 0xde, 0xd2, + 0xe2, 0x0b, 0x89, 0xe0, 0xb7, 0x2f, 0x25, 0x10, 0x87, 0xe0, 0x32, 0x82, 0xba, 0x60, 0xca, 0xb8, + 0x9c, 0x06, 0xb7, 0x3a, 0xf9, 0x49, 0x8c, 0xcf, 0xbf, 0xe9, 0x07, 0xbd, 0x15, 0xe6, 0xc8, 0xef, + 0x25, 0x30, 0x72, 0x3c, 0xd2, 0x1e, 0xa9, 0x5d, 0xe4, 0xb5, 0xda, 0x2e, 0x74, 0x94, 0xc5, 0x73, + 0x1f, 0x4d, 0xb4, 0xc9, 0x9f, 0x25, 0xa0, 0x74, 0x08, 0xab, 0x72, 0xb7, 0x1d, 0x4e, 0xe6, 0x50, + 0x9e, 0x5f, 0x9c, 0xa3, 0x83, 0xdc, 0x96, 0x58, 0x3a, 0xa7, 0xdc, 0x2c, 0xc7, 0x79, 0xe5, 0xb6, + 0x0b, 0x05, 0xd9, 0x05, 0xff, 0x47, 0x81, 0x50, 0xea, 0x96, 0x54, 0xc0, 0x95, 0xfb, 0x67, 0x82, + 0xa7, 0xed, 0x3c, 0xd0, 0x17, 0xbf, 0xde, 0xea, 0x19, 0x08, 0x56, 0x03, 0xae, 0x3c, 0x38, 0x1b, + 0x3e, 0xed, 0xf8, 0x49, 0x02, 0x93, 0x27, 0xbf, 0xd0, 0x5d, 0xdf, 0xcf, 0x13, 0x29, 0x94, 0x95, + 0x0b, 0x53, 0x24, 0x5a, 0xcb, 0x2f, 0xf7, 0x0e, 0xf3, 0xd2, 0xfe, 0x61, 0x5e, 0xfa, 0x79, 0x98, + 0x97, 0xde, 0x1d, 0xe5, 0x73, 0xfb, 0x47, 0xf9, 0xdc, 0xf7, 0xa3, 0x7c, 0xee, 0xd5, 0x93, 0xe3, + 0x71, 0xd0, 0xec, 0x5a, 0x4a, 0xbf, 0xb8, 0xc2, 0x7b, 0xda, 0x76, 0xeb, 0x67, 0x97, 0x48, 0x0a, + 0xab, 0x4f, 0x7c, 0xb4, 0xdc, 0xfd, 0x13, 0x00, 0x00, 0xff, 0xff, 0x76, 0xb7, 0xbf, 0x16, 0xa7, + 0x09, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -341,6 +596,9 @@ type MsgClient interface { AssignConsumerKey(ctx context.Context, in *MsgAssignConsumerKey, opts ...grpc.CallOption) (*MsgAssignConsumerKeyResponse, error) SubmitConsumerMisbehaviour(ctx context.Context, in *MsgSubmitConsumerMisbehaviour, opts ...grpc.CallOption) (*MsgSubmitConsumerMisbehaviourResponse, error) SubmitConsumerDoubleVoting(ctx context.Context, in *MsgSubmitConsumerDoubleVoting, opts ...grpc.CallOption) (*MsgSubmitConsumerDoubleVotingResponse, error) + OptIn(ctx context.Context, in *MsgOptIn, opts ...grpc.CallOption) (*MsgOptInResponse, error) + OptOut(ctx context.Context, in *MsgOptOut, opts ...grpc.CallOption) (*MsgOptOutResponse, error) + SetConsumerCommissionRate(ctx context.Context, in *MsgSetConsumerCommissionRate, opts ...grpc.CallOption) (*MsgSetConsumerCommissionRateResponse, error) } type msgClient struct { @@ -378,11 +636,41 @@ func (c *msgClient) SubmitConsumerDoubleVoting(ctx context.Context, in *MsgSubmi return out, nil } +func (c *msgClient) OptIn(ctx context.Context, in *MsgOptIn, opts ...grpc.CallOption) (*MsgOptInResponse, error) { + out := new(MsgOptInResponse) + err := c.cc.Invoke(ctx, "/interchain_security.ccv.provider.v1.Msg/OptIn", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) OptOut(ctx context.Context, in *MsgOptOut, opts ...grpc.CallOption) (*MsgOptOutResponse, error) { + out := new(MsgOptOutResponse) + err := c.cc.Invoke(ctx, "/interchain_security.ccv.provider.v1.Msg/OptOut", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) SetConsumerCommissionRate(ctx context.Context, in *MsgSetConsumerCommissionRate, opts ...grpc.CallOption) (*MsgSetConsumerCommissionRateResponse, error) { + out := new(MsgSetConsumerCommissionRateResponse) + err := c.cc.Invoke(ctx, "/interchain_security.ccv.provider.v1.Msg/SetConsumerCommissionRate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { AssignConsumerKey(context.Context, *MsgAssignConsumerKey) (*MsgAssignConsumerKeyResponse, error) SubmitConsumerMisbehaviour(context.Context, *MsgSubmitConsumerMisbehaviour) (*MsgSubmitConsumerMisbehaviourResponse, error) SubmitConsumerDoubleVoting(context.Context, *MsgSubmitConsumerDoubleVoting) (*MsgSubmitConsumerDoubleVotingResponse, error) + OptIn(context.Context, *MsgOptIn) (*MsgOptInResponse, error) + OptOut(context.Context, *MsgOptOut) (*MsgOptOutResponse, error) + SetConsumerCommissionRate(context.Context, *MsgSetConsumerCommissionRate) (*MsgSetConsumerCommissionRateResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -398,6 +686,15 @@ func (*UnimplementedMsgServer) SubmitConsumerMisbehaviour(ctx context.Context, r func (*UnimplementedMsgServer) SubmitConsumerDoubleVoting(ctx context.Context, req *MsgSubmitConsumerDoubleVoting) (*MsgSubmitConsumerDoubleVotingResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SubmitConsumerDoubleVoting not implemented") } +func (*UnimplementedMsgServer) OptIn(ctx context.Context, req *MsgOptIn) (*MsgOptInResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method OptIn not implemented") +} +func (*UnimplementedMsgServer) OptOut(ctx context.Context, req *MsgOptOut) (*MsgOptOutResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method OptOut not implemented") +} +func (*UnimplementedMsgServer) SetConsumerCommissionRate(ctx context.Context, req *MsgSetConsumerCommissionRate) (*MsgSetConsumerCommissionRateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetConsumerCommissionRate not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -457,6 +754,60 @@ func _Msg_SubmitConsumerDoubleVoting_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _Msg_OptIn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgOptIn) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).OptIn(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/interchain_security.ccv.provider.v1.Msg/OptIn", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).OptIn(ctx, req.(*MsgOptIn)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_OptOut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgOptOut) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).OptOut(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/interchain_security.ccv.provider.v1.Msg/OptOut", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).OptOut(ctx, req.(*MsgOptOut)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_SetConsumerCommissionRate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSetConsumerCommissionRate) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SetConsumerCommissionRate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/interchain_security.ccv.provider.v1.Msg/SetConsumerCommissionRate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SetConsumerCommissionRate(ctx, req.(*MsgSetConsumerCommissionRate)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "interchain_security.ccv.provider.v1.Msg", HandlerType: (*MsgServer)(nil), @@ -473,6 +824,18 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "SubmitConsumerDoubleVoting", Handler: _Msg_SubmitConsumerDoubleVoting_Handler, }, + { + MethodName: "OptIn", + Handler: _Msg_OptIn_Handler, + }, + { + MethodName: "OptOut", + Handler: _Msg_OptOut_Handler, + }, + { + MethodName: "SetConsumerCommissionRate", + Handler: _Msg_SetConsumerCommissionRate_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "interchain_security/ccv/provider/v1/tx.proto", @@ -651,146 +1014,995 @@ func (m *MsgSubmitConsumerDoubleVoting) MarshalToSizedBuffer(dAtA []byte) (int, i -= size i = encodeVarintTx(dAtA, i, uint64(size)) } - i-- - dAtA[i] = 0x12 - } - if len(m.Submitter) > 0 { - i -= len(m.Submitter) - copy(dAtA[i:], m.Submitter) - i = encodeVarintTx(dAtA, i, uint64(len(m.Submitter))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *MsgSubmitConsumerDoubleVotingResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *MsgSubmitConsumerDoubleVotingResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *MsgSubmitConsumerDoubleVotingResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - return len(dAtA) - i, nil -} - -func encodeVarintTx(dAtA []byte, offset int, v uint64) int { - offset -= sovTx(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *MsgAssignConsumerKey) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ChainId) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) - } - l = len(m.ProviderAddr) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) - } - l = len(m.ConsumerKey) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + i-- + dAtA[i] = 0x12 + } + if len(m.Submitter) > 0 { + i -= len(m.Submitter) + copy(dAtA[i:], m.Submitter) + i = encodeVarintTx(dAtA, i, uint64(len(m.Submitter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSubmitConsumerDoubleVotingResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSubmitConsumerDoubleVotingResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSubmitConsumerDoubleVotingResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgOptIn) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgOptIn) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgOptIn) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ConsumerKey) > 0 { + i -= len(m.ConsumerKey) + copy(dAtA[i:], m.ConsumerKey) + i = encodeVarintTx(dAtA, i, uint64(len(m.ConsumerKey))) + i-- + dAtA[i] = 0x1a + } + if len(m.ProviderAddr) > 0 { + i -= len(m.ProviderAddr) + copy(dAtA[i:], m.ProviderAddr) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProviderAddr))) + i-- + dAtA[i] = 0x12 + } + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgOptInResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgOptInResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgOptInResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgOptOut) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgOptOut) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgOptOut) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ProviderAddr) > 0 { + i -= len(m.ProviderAddr) + copy(dAtA[i:], m.ProviderAddr) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProviderAddr))) + i-- + dAtA[i] = 0x12 + } + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgOptOutResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgOptOutResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgOptOutResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgSetConsumerCommissionRate) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSetConsumerCommissionRate) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSetConsumerCommissionRate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Rate.Size() + i -= size + if _, err := m.Rate.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0x12 + } + if len(m.ProviderAddr) > 0 { + i -= len(m.ProviderAddr) + copy(dAtA[i:], m.ProviderAddr) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProviderAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSetConsumerCommissionRateResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSetConsumerCommissionRateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSetConsumerCommissionRateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgAssignConsumerKey) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ProviderAddr) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ConsumerKey) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgAssignConsumerKeyResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgSubmitConsumerMisbehaviour) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Submitter) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Misbehaviour != nil { + l = m.Misbehaviour.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgSubmitConsumerMisbehaviourResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgSubmitConsumerDoubleVoting) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Submitter) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.DuplicateVoteEvidence != nil { + l = m.DuplicateVoteEvidence.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.InfractionBlockHeader != nil { + l = m.InfractionBlockHeader.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgSubmitConsumerDoubleVotingResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgOptIn) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ProviderAddr) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ConsumerKey) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgOptInResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgOptOut) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ProviderAddr) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgOptOutResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgSetConsumerCommissionRate) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ProviderAddr) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Rate.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgSetConsumerCommissionRateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgAssignConsumerKey) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgAssignConsumerKey: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAssignConsumerKey: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProviderAddr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProviderAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsumerKey", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConsumerKey = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgAssignConsumerKeyResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgAssignConsumerKeyResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAssignConsumerKeyResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSubmitConsumerMisbehaviour) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSubmitConsumerMisbehaviour: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSubmitConsumerMisbehaviour: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Submitter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Submitter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Misbehaviour", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Misbehaviour == nil { + m.Misbehaviour = &_07_tendermint.Misbehaviour{} + } + if err := m.Misbehaviour.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSubmitConsumerMisbehaviourResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSubmitConsumerMisbehaviourResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSubmitConsumerMisbehaviourResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSubmitConsumerDoubleVoting) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSubmitConsumerDoubleVoting: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSubmitConsumerDoubleVoting: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Submitter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Submitter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DuplicateVoteEvidence", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.DuplicateVoteEvidence == nil { + m.DuplicateVoteEvidence = &types.DuplicateVoteEvidence{} + } + if err := m.DuplicateVoteEvidence.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InfractionBlockHeader", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.InfractionBlockHeader == nil { + m.InfractionBlockHeader = &_07_tendermint.Header{} + } + if err := m.InfractionBlockHeader.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } } - return n -} - -func (m *MsgAssignConsumerKeyResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - return n -} - -func (m *MsgSubmitConsumerMisbehaviour) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Submitter) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) - } - if m.Misbehaviour != nil { - l = m.Misbehaviour.Size() - n += 1 + l + sovTx(uint64(l)) - } - return n -} -func (m *MsgSubmitConsumerMisbehaviourResponse) Size() (n int) { - if m == nil { - return 0 + if iNdEx > l { + return io.ErrUnexpectedEOF } - var l int - _ = l - return n + return nil } - -func (m *MsgSubmitConsumerDoubleVoting) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Submitter) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) - } - if m.DuplicateVoteEvidence != nil { - l = m.DuplicateVoteEvidence.Size() - n += 1 + l + sovTx(uint64(l)) - } - if m.InfractionBlockHeader != nil { - l = m.InfractionBlockHeader.Size() - n += 1 + l + sovTx(uint64(l)) +func (m *MsgSubmitConsumerDoubleVotingResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSubmitConsumerDoubleVotingResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSubmitConsumerDoubleVotingResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } } - return n -} -func (m *MsgSubmitConsumerDoubleVotingResponse) Size() (n int) { - if m == nil { - return 0 + if iNdEx > l { + return io.ErrUnexpectedEOF } - var l int - _ = l - return n -} - -func sovTx(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozTx(x uint64) (n int) { - return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) + return nil } -func (m *MsgAssignConsumerKey) Unmarshal(dAtA []byte) error { +func (m *MsgOptIn) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -813,10 +2025,10 @@ func (m *MsgAssignConsumerKey) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgAssignConsumerKey: wiretype end group for non-group") + return fmt.Errorf("proto: MsgOptIn: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgAssignConsumerKey: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgOptIn: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -936,7 +2148,7 @@ func (m *MsgAssignConsumerKey) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgAssignConsumerKeyResponse) Unmarshal(dAtA []byte) error { +func (m *MsgOptInResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -959,10 +2171,10 @@ func (m *MsgAssignConsumerKeyResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgAssignConsumerKeyResponse: wiretype end group for non-group") + return fmt.Errorf("proto: MsgOptInResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgAssignConsumerKeyResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgOptInResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: @@ -986,7 +2198,7 @@ func (m *MsgAssignConsumerKeyResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgSubmitConsumerMisbehaviour) Unmarshal(dAtA []byte) error { +func (m *MsgOptOut) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1009,15 +2221,15 @@ func (m *MsgSubmitConsumerMisbehaviour) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgSubmitConsumerMisbehaviour: wiretype end group for non-group") + return fmt.Errorf("proto: MsgOptOut: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgSubmitConsumerMisbehaviour: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgOptOut: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Submitter", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -1045,13 +2257,13 @@ func (m *MsgSubmitConsumerMisbehaviour) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Submitter = string(dAtA[iNdEx:postIndex]) + m.ChainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Misbehaviour", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ProviderAddr", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -1061,27 +2273,23 @@ func (m *MsgSubmitConsumerMisbehaviour) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Misbehaviour == nil { - m.Misbehaviour = &_07_tendermint.Misbehaviour{} - } - if err := m.Misbehaviour.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.ProviderAddr = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -1104,7 +2312,7 @@ func (m *MsgSubmitConsumerMisbehaviour) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgSubmitConsumerMisbehaviourResponse) Unmarshal(dAtA []byte) error { +func (m *MsgOptOutResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1127,10 +2335,10 @@ func (m *MsgSubmitConsumerMisbehaviourResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgSubmitConsumerMisbehaviourResponse: wiretype end group for non-group") + return fmt.Errorf("proto: MsgOptOutResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgSubmitConsumerMisbehaviourResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgOptOutResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: @@ -1154,7 +2362,7 @@ func (m *MsgSubmitConsumerMisbehaviourResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgSubmitConsumerDoubleVoting) Unmarshal(dAtA []byte) error { +func (m *MsgSetConsumerCommissionRate) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1177,15 +2385,15 @@ func (m *MsgSubmitConsumerDoubleVoting) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgSubmitConsumerDoubleVoting: wiretype end group for non-group") + return fmt.Errorf("proto: MsgSetConsumerCommissionRate: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgSubmitConsumerDoubleVoting: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgSetConsumerCommissionRate: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Submitter", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ProviderAddr", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -1213,13 +2421,13 @@ func (m *MsgSubmitConsumerDoubleVoting) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Submitter = string(dAtA[iNdEx:postIndex]) + m.ProviderAddr = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DuplicateVoteEvidence", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -1229,33 +2437,29 @@ func (m *MsgSubmitConsumerDoubleVoting) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - if m.DuplicateVoteEvidence == nil { - m.DuplicateVoteEvidence = &types.DuplicateVoteEvidence{} - } - if err := m.DuplicateVoteEvidence.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.ChainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field InfractionBlockHeader", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Rate", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -1265,25 +2469,23 @@ func (m *MsgSubmitConsumerDoubleVoting) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - if m.InfractionBlockHeader == nil { - m.InfractionBlockHeader = &_07_tendermint.Header{} - } - if err := m.InfractionBlockHeader.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Rate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1308,7 +2510,7 @@ func (m *MsgSubmitConsumerDoubleVoting) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgSubmitConsumerDoubleVotingResponse) Unmarshal(dAtA []byte) error { +func (m *MsgSetConsumerCommissionRateResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1331,10 +2533,10 @@ func (m *MsgSubmitConsumerDoubleVotingResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgSubmitConsumerDoubleVotingResponse: wiretype end group for non-group") + return fmt.Errorf("proto: MsgSetConsumerCommissionRateResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgSubmitConsumerDoubleVotingResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgSetConsumerCommissionRateResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: diff --git a/x/ccv/types/expected_keepers.go b/x/ccv/types/expected_keepers.go index a2ef7ab465..b3815b6d65 100644 --- a/x/ccv/types/expected_keepers.go +++ b/x/ccv/types/expected_keepers.go @@ -15,6 +15,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" auth "github.com/cosmos/cosmos-sdk/x/auth/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -41,6 +42,9 @@ type StakingKeeper interface { IterateLastValidatorPowers(ctx sdk.Context, cb func(addr sdk.ValAddress, power int64) (stop bool)) PowerReduction(ctx sdk.Context) math.Int PutUnbondingOnHold(ctx sdk.Context, id uint64) error + GetUnbondingDelegationByUnbondingID(ctx sdk.Context, id uint64) (ubd stakingtypes.UnbondingDelegation, found bool) + GetRedelegationByUnbondingID(ctx sdk.Context, id uint64) (red stakingtypes.Redelegation, found bool) + GetValidatorByUnbondingID(ctx sdk.Context, id uint64) (val stakingtypes.Validator, found bool) IterateValidators(ctx sdk.Context, f func(index int64, validator stakingtypes.ValidatorI) (stop bool)) Validator(ctx sdk.Context, addr sdk.ValAddress) stakingtypes.ValidatorI IsValidatorJailed(ctx sdk.Context, addr sdk.ConsAddress) bool @@ -53,6 +57,7 @@ type StakingKeeper interface { GetUnbondingDelegationsFromValidator(ctx sdk.Context, valAddr sdk.ValAddress) (ubds []stakingtypes.UnbondingDelegation) GetRedelegationsFromSrcValidator(ctx sdk.Context, valAddr sdk.ValAddress) (reds []stakingtypes.Redelegation) GetUnbondingType(ctx sdk.Context, id uint64) (unbondingType stakingtypes.UnbondingType, found bool) + MinCommissionRate(ctx sdk.Context) math.LegacyDec } // SlashingKeeper defines the contract expected to perform ccv slashing @@ -109,6 +114,10 @@ type ClientKeeper interface { // DistributionKeeper defines the expected interface of the distribution keeper type DistributionKeeper interface { FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error + GetFeePool(ctx sdk.Context) distrtypes.FeePool + SetFeePool(ctx sdk.Context, feePool distrtypes.FeePool) + GetCommunityTax(ctx sdk.Context) math.LegacyDec + AllocateTokensToValidator(ctx sdk.Context, validator stakingtypes.ValidatorI, reward sdk.DecCoins) } // ConsumerHooks event hooks for newly bonded cross-chain validators diff --git a/x/ccv/types/utils_test.go b/x/ccv/types/utils_test.go index f7ecd83197..44f4ff6d27 100644 --- a/x/ccv/types/utils_test.go +++ b/x/ccv/types/utils_test.go @@ -10,7 +10,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/x/ccv/types" ) func TestAccumulateChanges(t *testing.T) { diff --git a/x/ccv/types/wire_test.go b/x/ccv/types/wire_test.go index ab6692912e..93512a5218 100644 --- a/x/ccv/types/wire_test.go +++ b/x/ccv/types/wire_test.go @@ -12,8 +12,8 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v4/testutil/crypto" - "github.com/cosmos/interchain-security/v4/x/ccv/types" + "github.com/cosmos/interchain-security/v5/testutil/crypto" + "github.com/cosmos/interchain-security/v5/x/ccv/types" ) func TestPacketDataValidateBasic(t *testing.T) { From d313ba5581f9168294b242012edfebe8f945ad28 Mon Sep 17 00:00:00 2001 From: insumity Date: Wed, 1 May 2024 18:04:58 +0200 Subject: [PATCH 013/102] chore: add v5.0.x-provider and v5.0.x-provider to mergify and dependabot (#1845) updated bots for v5.0.x-provider release --- .github/dependabot.yml | 12 +++++++++++- .mergify.yml | 8 ++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 382d63af3f..5356217e13 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -17,7 +17,7 @@ updates: open-pull-requests-limit: 10 labels: - dependencies - + - package-ecosystem: gomod directory: "/" schedule: @@ -77,3 +77,13 @@ updates: open-pull-requests-limit: 0 labels: - dependencies + + - package-ecosystem: gomod + directory: "/" + schedule: + interval: daily + target-branch: "release/v5.0.x-provider" + # Only allow automated security-related dependency updates on release branches. + open-pull-requests-limit: 0 + labels: + - dependencies diff --git a/.mergify.yml b/.mergify.yml index 8679f042eb..11a69816ce 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -58,3 +58,11 @@ pull_request_rules: backport: branches: - release/v5.x + - name: Backport patches to the release/v5.0.x-provider branch + conditions: + - base=main + - label=A:backport/v5.0.x-provider + actions: + backport: + branches: + - release/v5.0.x-provider From 2b5b6cfba035e942bd35fa32c2da8d8b26a6b487 Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Thu, 2 May 2024 09:47:15 +0200 Subject: [PATCH 014/102] fix: revert version bump to v5 (#1847) * revert bump version to 5 * register migration --- .../ante/forbidden_proposals_ante_test.go | 4 ++-- app/consumer-democracy/ante_handler.go | 6 +++--- app/consumer-democracy/app.go | 16 ++++++++-------- .../proposals_whitelisting_test.go | 6 +++--- app/consumer/ante/disabled_modules_ante_test.go | 4 ++-- app/consumer/ante/msg_filter_ante_test.go | 4 ++-- app/consumer/ante_handler.go | 4 ++-- app/consumer/app.go | 10 +++++----- app/consumer/genesis.go | 4 ++-- app/consumer/genesis_test.go | 6 +++--- app/provider/app.go | 14 +++++++------- app/sovereign/app.go | 4 ++-- cmd/interchain-security-cd/cmd/root.go | 4 ++-- cmd/interchain-security-cd/main.go | 6 +++--- cmd/interchain-security-cdd/cmd/root.go | 4 ++-- cmd/interchain-security-cdd/main.go | 6 +++--- cmd/interchain-security-pd/cmd/root.go | 4 ++-- cmd/interchain-security-pd/main.go | 6 +++--- cmd/interchain-security-sd/cmd/root.go | 4 ++-- cmd/interchain-security-sd/main.go | 6 +++--- go.mod | 2 +- .../ccv/consumer/v1/consumer.proto | 2 +- .../ccv/consumer/v1/genesis.proto | 2 +- .../ccv/consumer/v1/query.proto | 2 +- .../ccv/provider/v1/genesis.proto | 2 +- .../ccv/provider/v1/provider.proto | 2 +- .../ccv/provider/v1/query.proto | 2 +- .../interchain_security/ccv/provider/v1/tx.proto | 2 +- .../ccv/v1/shared_consumer.proto | 2 +- proto/interchain_security/ccv/v1/wire.proto | 2 +- scripts/protocgen.sh | 2 +- tests/e2e/actions.go | 6 +++--- tests/integration/common.go | 8 ++++---- tests/integration/democracy.go | 6 +++--- tests/integration/distribution.go | 12 ++++++------ tests/integration/double_vote.go | 4 ++-- tests/integration/expired_client.go | 2 +- tests/integration/instance_test.go | 10 +++++----- tests/integration/key_assignment.go | 4 ++-- tests/integration/misbehaviour.go | 4 ++-- tests/integration/normal_operations.go | 4 ++-- tests/integration/setup.go | 12 ++++++------ tests/integration/slashing.go | 6 +++--- tests/integration/soft_opt_out.go | 4 ++-- tests/integration/stop_consumer.go | 2 +- tests/integration/throttle.go | 8 ++++---- tests/integration/throttle_retry.go | 2 +- tests/integration/unbonding.go | 4 ++-- tests/integration/valset_update.go | 2 +- tests/mbt/driver/core.go | 16 ++++++++-------- tests/mbt/driver/mbt_test.go | 6 +++--- tests/mbt/driver/setup.go | 12 ++++++------ testutil/crypto/crypto.go | 2 +- testutil/ibc_testing/generic_setup.go | 8 ++++---- testutil/ibc_testing/specific_setup.go | 10 +++++----- testutil/integration/debug_test.go | 10 +++++----- testutil/integration/interfaces.go | 6 +++--- testutil/keeper/expectations.go | 4 ++-- testutil/keeper/unit_test_helpers.go | 10 +++++----- x/ccv/consumer/client/cli/query.go | 2 +- x/ccv/consumer/ibc_module.go | 6 +++--- x/ccv/consumer/ibc_module_test.go | 8 ++++---- x/ccv/consumer/keeper/changeover_test.go | 4 ++-- x/ccv/consumer/keeper/distribution.go | 4 ++-- x/ccv/consumer/keeper/distribution_test.go | 6 +++--- x/ccv/consumer/keeper/genesis.go | 4 ++-- x/ccv/consumer/keeper/genesis_test.go | 10 +++++----- x/ccv/consumer/keeper/grpc_query.go | 4 ++-- x/ccv/consumer/keeper/hooks.go | 2 +- x/ccv/consumer/keeper/keeper.go | 4 ++-- x/ccv/consumer/keeper/keeper_test.go | 8 ++++---- x/ccv/consumer/keeper/migrations.go | 2 +- x/ccv/consumer/keeper/params.go | 2 +- x/ccv/consumer/keeper/params_test.go | 4 ++-- x/ccv/consumer/keeper/provider_info.go | 4 ++-- x/ccv/consumer/keeper/relay.go | 4 ++-- x/ccv/consumer/keeper/relay_test.go | 10 +++++----- x/ccv/consumer/keeper/soft_opt_out.go | 2 +- x/ccv/consumer/keeper/soft_opt_out_test.go | 6 +++--- x/ccv/consumer/keeper/throttle_retry.go | 2 +- x/ccv/consumer/keeper/throttle_retry_test.go | 6 +++--- x/ccv/consumer/keeper/validators.go | 2 +- x/ccv/consumer/keeper/validators_test.go | 8 ++++---- x/ccv/consumer/migrations/v2/migration.go | 4 ++-- x/ccv/consumer/migrations/v2/migration_test.go | 8 ++++---- x/ccv/consumer/module.go | 8 ++++---- x/ccv/consumer/types/genesis.go | 2 +- x/ccv/consumer/types/genesis.pb.go | 2 +- x/ccv/consumer/types/genesis_test.go | 6 +++--- x/ccv/consumer/types/keys.go | 2 +- x/ccv/consumer/types/params_test.go | 2 +- x/ccv/consumer/types/query.pb.go | 2 +- x/ccv/democracy/distribution/module.go | 2 +- x/ccv/provider/client/cli/query.go | 2 +- x/ccv/provider/client/cli/tx.go | 2 +- x/ccv/provider/client/proposal_handler.go | 2 +- x/ccv/provider/handler.go | 4 ++-- x/ccv/provider/handler_test.go | 10 +++++----- x/ccv/provider/ibc_middleware.go | 4 ++-- x/ccv/provider/ibc_middleware_test.go | 2 +- x/ccv/provider/ibc_module.go | 6 +++--- x/ccv/provider/ibc_module_test.go | 10 +++++----- x/ccv/provider/keeper/consumer_equivocation.go | 4 ++-- .../keeper/consumer_equivocation_test.go | 6 +++--- x/ccv/provider/keeper/distribution.go | 2 +- x/ccv/provider/keeper/distribution_test.go | 6 +++--- x/ccv/provider/keeper/genesis.go | 4 ++-- x/ccv/provider/keeper/genesis_test.go | 10 +++++----- x/ccv/provider/keeper/grpc_query.go | 4 ++-- x/ccv/provider/keeper/grpc_query_test.go | 8 ++++---- x/ccv/provider/keeper/hooks.go | 6 +++--- x/ccv/provider/keeper/hooks_test.go | 6 +++--- x/ccv/provider/keeper/keeper.go | 6 +++--- x/ccv/provider/keeper/keeper_test.go | 8 ++++---- x/ccv/provider/keeper/key_assignment.go | 4 ++-- x/ccv/provider/keeper/key_assignment_test.go | 10 +++++----- x/ccv/provider/keeper/msg_server.go | 4 ++-- x/ccv/provider/keeper/params.go | 4 ++-- x/ccv/provider/keeper/params_test.go | 4 ++-- x/ccv/provider/keeper/partial_set_security.go | 2 +- .../provider/keeper/partial_set_security_test.go | 8 ++++---- x/ccv/provider/keeper/proposal.go | 4 ++-- x/ccv/provider/keeper/proposal_test.go | 10 +++++----- x/ccv/provider/keeper/relay.go | 4 ++-- x/ccv/provider/keeper/relay_test.go | 14 +++++++------- x/ccv/provider/keeper/throttle.go | 2 +- x/ccv/provider/keeper/throttle_legacy.go | 4 ++-- x/ccv/provider/keeper/throttle_test.go | 4 ++-- x/ccv/provider/keeper/validator_set_update.go | 2 +- .../provider/keeper/validator_set_update_test.go | 8 ++++---- x/ccv/provider/migrations/migrator.go | 12 ++++++------ x/ccv/provider/migrations/v3/migration_test.go | 2 +- x/ccv/provider/migrations/v3/migrations.go | 2 +- x/ccv/provider/migrations/v4/migration_test.go | 4 ++-- x/ccv/provider/migrations/v4/migrations.go | 2 +- x/ccv/provider/migrations/v5/migration_test.go | 2 +- x/ccv/provider/migrations/v5/migrations.go | 2 +- x/ccv/provider/module.go | 11 +++++++---- x/ccv/provider/module_test.go | 8 ++++---- x/ccv/provider/proposal_handler.go | 4 ++-- x/ccv/provider/proposal_handler_test.go | 6 +++--- x/ccv/provider/types/consumer.go | 2 +- x/ccv/provider/types/genesis.go | 2 +- x/ccv/provider/types/genesis.pb.go | 2 +- x/ccv/provider/types/genesis_test.go | 6 +++--- x/ccv/provider/types/key_assignment.go | 2 +- x/ccv/provider/types/keys.go | 2 +- x/ccv/provider/types/keys_test.go | 4 ++-- x/ccv/provider/types/msg.go | 2 +- x/ccv/provider/types/params.go | 2 +- x/ccv/provider/types/params_test.go | 2 +- x/ccv/provider/types/proposal.go | 2 +- x/ccv/provider/types/proposal_test.go | 2 +- x/ccv/provider/types/provider.pb.go | 2 +- x/ccv/provider/types/query.pb.go | 2 +- x/ccv/types/utils_test.go | 2 +- x/ccv/types/wire_test.go | 4 ++-- 157 files changed, 389 insertions(+), 386 deletions(-) diff --git a/app/consumer-democracy/ante/forbidden_proposals_ante_test.go b/app/consumer-democracy/ante/forbidden_proposals_ante_test.go index c4c7123290..8fd7fe3824 100644 --- a/app/consumer-democracy/ante/forbidden_proposals_ante_test.go +++ b/app/consumer-democracy/ante/forbidden_proposals_ante_test.go @@ -14,8 +14,8 @@ import ( minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" "github.com/cosmos/cosmos-sdk/x/params/types/proposal" - app "github.com/cosmos/interchain-security/v5/app/consumer-democracy" - "github.com/cosmos/interchain-security/v5/app/consumer-democracy/ante" + app "github.com/cosmos/interchain-security/v4/app/consumer-democracy" + "github.com/cosmos/interchain-security/v4/app/consumer-democracy/ante" ) // in SDKv47 parameter updates full params object is required diff --git a/app/consumer-democracy/ante_handler.go b/app/consumer-democracy/ante_handler.go index b0f7432fb6..7e652ebb0c 100644 --- a/app/consumer-democracy/ante_handler.go +++ b/app/consumer-democracy/ante_handler.go @@ -10,9 +10,9 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" - democracyante "github.com/cosmos/interchain-security/v5/app/consumer-democracy/ante" - consumerante "github.com/cosmos/interchain-security/v5/app/consumer/ante" - ibcconsumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" + democracyante "github.com/cosmos/interchain-security/v4/app/consumer-democracy/ante" + consumerante "github.com/cosmos/interchain-security/v4/app/consumer/ante" + ibcconsumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" ) // HandlerOptions extend the SDK's AnteHandler options by requiring the IBC diff --git a/app/consumer-democracy/app.go b/app/consumer-democracy/app.go index 190e6680d8..e90d35760e 100644 --- a/app/consumer-democracy/app.go +++ b/app/consumer-democracy/app.go @@ -103,14 +103,14 @@ import ( "github.com/cometbft/cometbft/libs/log" tmos "github.com/cometbft/cometbft/libs/os" - appencoding "github.com/cosmos/interchain-security/v5/app/encoding" - testutil "github.com/cosmos/interchain-security/v5/testutil/integration" - consumer "github.com/cosmos/interchain-security/v5/x/ccv/consumer" - consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccvdistr "github.com/cosmos/interchain-security/v5/x/ccv/democracy/distribution" - ccvgov "github.com/cosmos/interchain-security/v5/x/ccv/democracy/governance" - ccvstaking "github.com/cosmos/interchain-security/v5/x/ccv/democracy/staking" + appencoding "github.com/cosmos/interchain-security/v4/app/encoding" + testutil "github.com/cosmos/interchain-security/v4/testutil/integration" + consumer "github.com/cosmos/interchain-security/v4/x/ccv/consumer" + consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccvdistr "github.com/cosmos/interchain-security/v4/x/ccv/democracy/distribution" + ccvgov "github.com/cosmos/interchain-security/v4/x/ccv/democracy/governance" + ccvstaking "github.com/cosmos/interchain-security/v4/x/ccv/democracy/staking" ) const ( diff --git a/app/consumer-democracy/proposals_whitelisting_test.go b/app/consumer-democracy/proposals_whitelisting_test.go index c4209bf3b9..47dd42317a 100644 --- a/app/consumer-democracy/proposals_whitelisting_test.go +++ b/app/consumer-democracy/proposals_whitelisting_test.go @@ -6,9 +6,9 @@ import ( ibctesting "github.com/cosmos/ibc-go/v7/testing" "github.com/stretchr/testify/require" - appConsumer "github.com/cosmos/interchain-security/v5/app/consumer-democracy" - icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" - testutil "github.com/cosmos/interchain-security/v5/testutil/integration" + appConsumer "github.com/cosmos/interchain-security/v4/app/consumer-democracy" + icstestingutils "github.com/cosmos/interchain-security/v4/testutil/ibc_testing" + testutil "github.com/cosmos/interchain-security/v4/testutil/integration" ) func TestDemocracyGovernanceWhitelistingKeys(t *testing.T) { diff --git a/app/consumer/ante/disabled_modules_ante_test.go b/app/consumer/ante/disabled_modules_ante_test.go index d06c3ad595..7fa95f37c6 100644 --- a/app/consumer/ante/disabled_modules_ante_test.go +++ b/app/consumer/ante/disabled_modules_ante_test.go @@ -12,8 +12,8 @@ import ( evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" - "github.com/cosmos/interchain-security/v5/app/consumer/ante" - appencoding "github.com/cosmos/interchain-security/v5/app/encoding" + "github.com/cosmos/interchain-security/v4/app/consumer/ante" + appencoding "github.com/cosmos/interchain-security/v4/app/encoding" ) func TestDisabledModulesDecorator(t *testing.T) { diff --git a/app/consumer/ante/msg_filter_ante_test.go b/app/consumer/ante/msg_filter_ante_test.go index 9dd5f47ef3..bfc1bb0a50 100644 --- a/app/consumer/ante/msg_filter_ante_test.go +++ b/app/consumer/ante/msg_filter_ante_test.go @@ -9,8 +9,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/cosmos/interchain-security/v5/app/consumer/ante" - appencoding "github.com/cosmos/interchain-security/v5/app/encoding" + "github.com/cosmos/interchain-security/v4/app/consumer/ante" + appencoding "github.com/cosmos/interchain-security/v4/app/encoding" ) type consumerKeeper struct { diff --git a/app/consumer/ante_handler.go b/app/consumer/ante_handler.go index f9cd986dad..fa28a52caf 100644 --- a/app/consumer/ante_handler.go +++ b/app/consumer/ante_handler.go @@ -10,8 +10,8 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" - consumerante "github.com/cosmos/interchain-security/v5/app/consumer/ante" - ibcconsumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" + consumerante "github.com/cosmos/interchain-security/v4/app/consumer/ante" + ibcconsumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" ) // HandlerOptions extend the SDK's AnteHandler options by requiring the IBC diff --git a/app/consumer/app.go b/app/consumer/app.go index 207939adcb..9853145117 100644 --- a/app/consumer/app.go +++ b/app/consumer/app.go @@ -87,11 +87,11 @@ import ( "github.com/cometbft/cometbft/libs/log" tmos "github.com/cometbft/cometbft/libs/os" - appencoding "github.com/cosmos/interchain-security/v5/app/encoding" - testutil "github.com/cosmos/interchain-security/v5/testutil/integration" - ibcconsumer "github.com/cosmos/interchain-security/v5/x/ccv/consumer" - ibcconsumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" - ibcconsumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + appencoding "github.com/cosmos/interchain-security/v4/app/encoding" + testutil "github.com/cosmos/interchain-security/v4/testutil/integration" + ibcconsumer "github.com/cosmos/interchain-security/v4/x/ccv/consumer" + ibcconsumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + ibcconsumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" ) const ( diff --git a/app/consumer/genesis.go b/app/consumer/genesis.go index da55a0334e..f6c8e47905 100644 --- a/app/consumer/genesis.go +++ b/app/consumer/genesis.go @@ -16,8 +16,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" - consumerTypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - "github.com/cosmos/interchain-security/v5/x/ccv/types" + consumerTypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // The genesis state of the blockchain is represented here as a map of raw json diff --git a/app/consumer/genesis_test.go b/app/consumer/genesis_test.go index 2dd29e1b71..5827651959 100644 --- a/app/consumer/genesis_test.go +++ b/app/consumer/genesis_test.go @@ -16,9 +16,9 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/x/auth/types" - app "github.com/cosmos/interchain-security/v5/app/consumer" - consumerTypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + app "github.com/cosmos/interchain-security/v4/app/consumer" + consumerTypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) const ( diff --git a/app/provider/app.go b/app/provider/app.go index e5baa31270..9cf45fe7e3 100644 --- a/app/provider/app.go +++ b/app/provider/app.go @@ -100,13 +100,13 @@ import ( "github.com/cometbft/cometbft/libs/log" tmos "github.com/cometbft/cometbft/libs/os" - appencoding "github.com/cosmos/interchain-security/v5/app/encoding" - testutil "github.com/cosmos/interchain-security/v5/testutil/integration" - "github.com/cosmos/interchain-security/v5/x/ccv/provider" - ibcprovider "github.com/cosmos/interchain-security/v5/x/ccv/provider" - ibcproviderclient "github.com/cosmos/interchain-security/v5/x/ccv/provider/client" - ibcproviderkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + appencoding "github.com/cosmos/interchain-security/v4/app/encoding" + testutil "github.com/cosmos/interchain-security/v4/testutil/integration" + "github.com/cosmos/interchain-security/v4/x/ccv/provider" + ibcprovider "github.com/cosmos/interchain-security/v4/x/ccv/provider" + ibcproviderclient "github.com/cosmos/interchain-security/v4/x/ccv/provider/client" + ibcproviderkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) const ( diff --git a/app/sovereign/app.go b/app/sovereign/app.go index 6dd9774f3b..4017ca9d4f 100644 --- a/app/sovereign/app.go +++ b/app/sovereign/app.go @@ -106,8 +106,8 @@ import ( "github.com/cometbft/cometbft/libs/log" tmos "github.com/cometbft/cometbft/libs/os" - appencoding "github.com/cosmos/interchain-security/v5/app/encoding" - testutil "github.com/cosmos/interchain-security/v5/testutil/integration" + appencoding "github.com/cosmos/interchain-security/v4/app/encoding" + testutil "github.com/cosmos/interchain-security/v4/testutil/integration" ) const ( diff --git a/cmd/interchain-security-cd/cmd/root.go b/cmd/interchain-security-cd/cmd/root.go index 3d844d9c7c..215b45317a 100644 --- a/cmd/interchain-security-cd/cmd/root.go +++ b/cmd/interchain-security-cd/cmd/root.go @@ -31,8 +31,8 @@ import ( tmcfg "github.com/cometbft/cometbft/config" "github.com/cometbft/cometbft/libs/log" - consumer "github.com/cosmos/interchain-security/v5/app/consumer" - appencoding "github.com/cosmos/interchain-security/v5/app/encoding" + consumer "github.com/cosmos/interchain-security/v4/app/consumer" + appencoding "github.com/cosmos/interchain-security/v4/app/encoding" ) // NewRootCmd creates a new root command for simd. It is called once in the diff --git a/cmd/interchain-security-cd/main.go b/cmd/interchain-security-cd/main.go index 7cbe0a898a..a64a2a8645 100644 --- a/cmd/interchain-security-cd/main.go +++ b/cmd/interchain-security-cd/main.go @@ -6,9 +6,9 @@ import ( "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" - app "github.com/cosmos/interchain-security/v5/app/consumer" - appparams "github.com/cosmos/interchain-security/v5/app/params" - "github.com/cosmos/interchain-security/v5/cmd/interchain-security-cd/cmd" + app "github.com/cosmos/interchain-security/v4/app/consumer" + appparams "github.com/cosmos/interchain-security/v4/app/params" + "github.com/cosmos/interchain-security/v4/cmd/interchain-security-cd/cmd" ) func main() { diff --git a/cmd/interchain-security-cdd/cmd/root.go b/cmd/interchain-security-cdd/cmd/root.go index cb5140ade6..6b2e6cc726 100644 --- a/cmd/interchain-security-cdd/cmd/root.go +++ b/cmd/interchain-security-cdd/cmd/root.go @@ -31,8 +31,8 @@ import ( tmcfg "github.com/cometbft/cometbft/config" "github.com/cometbft/cometbft/libs/log" - cdd "github.com/cosmos/interchain-security/v5/app/consumer-democracy" - appencoding "github.com/cosmos/interchain-security/v5/app/encoding" + cdd "github.com/cosmos/interchain-security/v4/app/consumer-democracy" + appencoding "github.com/cosmos/interchain-security/v4/app/encoding" ) // NewRootCmd creates a new root command for simd. It is called once in the diff --git a/cmd/interchain-security-cdd/main.go b/cmd/interchain-security-cdd/main.go index 9fbd09df75..9b6aacd759 100644 --- a/cmd/interchain-security-cdd/main.go +++ b/cmd/interchain-security-cdd/main.go @@ -6,9 +6,9 @@ import ( "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" - app "github.com/cosmos/interchain-security/v5/app/consumer-democracy" - appparams "github.com/cosmos/interchain-security/v5/app/params" - "github.com/cosmos/interchain-security/v5/cmd/interchain-security-cdd/cmd" + app "github.com/cosmos/interchain-security/v4/app/consumer-democracy" + appparams "github.com/cosmos/interchain-security/v4/app/params" + "github.com/cosmos/interchain-security/v4/cmd/interchain-security-cdd/cmd" ) func main() { diff --git a/cmd/interchain-security-pd/cmd/root.go b/cmd/interchain-security-pd/cmd/root.go index 7f1b0a6b9a..8a8f5fec32 100644 --- a/cmd/interchain-security-pd/cmd/root.go +++ b/cmd/interchain-security-pd/cmd/root.go @@ -31,8 +31,8 @@ import ( tmcfg "github.com/cometbft/cometbft/config" "github.com/cometbft/cometbft/libs/log" - appencoding "github.com/cosmos/interchain-security/v5/app/encoding" - providerApp "github.com/cosmos/interchain-security/v5/app/provider" + appencoding "github.com/cosmos/interchain-security/v4/app/encoding" + providerApp "github.com/cosmos/interchain-security/v4/app/provider" ) // NewRootCmd creates a new root command for simd. It is called once in the diff --git a/cmd/interchain-security-pd/main.go b/cmd/interchain-security-pd/main.go index dab89c91fc..7788f06bff 100644 --- a/cmd/interchain-security-pd/main.go +++ b/cmd/interchain-security-pd/main.go @@ -6,9 +6,9 @@ import ( "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" - appparams "github.com/cosmos/interchain-security/v5/app/params" - app "github.com/cosmos/interchain-security/v5/app/provider" - "github.com/cosmos/interchain-security/v5/cmd/interchain-security-pd/cmd" + appparams "github.com/cosmos/interchain-security/v4/app/params" + app "github.com/cosmos/interchain-security/v4/app/provider" + "github.com/cosmos/interchain-security/v4/cmd/interchain-security-pd/cmd" ) func main() { diff --git a/cmd/interchain-security-sd/cmd/root.go b/cmd/interchain-security-sd/cmd/root.go index 10a79a251a..28520ef1a4 100644 --- a/cmd/interchain-security-sd/cmd/root.go +++ b/cmd/interchain-security-sd/cmd/root.go @@ -31,8 +31,8 @@ import ( tmcfg "github.com/cometbft/cometbft/config" "github.com/cometbft/cometbft/libs/log" - appencoding "github.com/cosmos/interchain-security/v5/app/encoding" - sovereignApp "github.com/cosmos/interchain-security/v5/app/sovereign" + appencoding "github.com/cosmos/interchain-security/v4/app/encoding" + sovereignApp "github.com/cosmos/interchain-security/v4/app/sovereign" ) // NewRootCmd creates a new root command for simd. It is called once in the diff --git a/cmd/interchain-security-sd/main.go b/cmd/interchain-security-sd/main.go index e719eb48f3..2265afad90 100644 --- a/cmd/interchain-security-sd/main.go +++ b/cmd/interchain-security-sd/main.go @@ -6,9 +6,9 @@ import ( "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" - appparams "github.com/cosmos/interchain-security/v5/app/params" - app "github.com/cosmos/interchain-security/v5/app/sovereign" - "github.com/cosmos/interchain-security/v5/cmd/interchain-security-sd/cmd" + appparams "github.com/cosmos/interchain-security/v4/app/params" + app "github.com/cosmos/interchain-security/v4/app/sovereign" + "github.com/cosmos/interchain-security/v4/cmd/interchain-security-sd/cmd" ) func main() { diff --git a/go.mod b/go.mod index 4a5ddd9493..db8b94586c 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/cosmos/interchain-security/v5 +module github.com/cosmos/interchain-security/v4 go 1.21.1 diff --git a/proto/interchain_security/ccv/consumer/v1/consumer.proto b/proto/interchain_security/ccv/consumer/v1/consumer.proto index 959d06c087..749eedc7ac 100644 --- a/proto/interchain_security/ccv/consumer/v1/consumer.proto +++ b/proto/interchain_security/ccv/consumer/v1/consumer.proto @@ -1,7 +1,7 @@ syntax = "proto3"; package interchain_security.ccv.consumer.v1; -option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types"; +option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types"; import "google/protobuf/any.proto"; import "gogoproto/gogo.proto"; diff --git a/proto/interchain_security/ccv/consumer/v1/genesis.proto b/proto/interchain_security/ccv/consumer/v1/genesis.proto index 642b78451d..a2ceb0f9f6 100644 --- a/proto/interchain_security/ccv/consumer/v1/genesis.proto +++ b/proto/interchain_security/ccv/consumer/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package interchain_security.ccv.consumer.v1; -option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types"; +option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types"; import "interchain_security/ccv/v1/shared_consumer.proto"; import "ibc/lightclients/tendermint/v1/tendermint.proto"; diff --git a/proto/interchain_security/ccv/consumer/v1/query.proto b/proto/interchain_security/ccv/consumer/v1/query.proto index 0e9b088e1d..eb8eb29a3d 100644 --- a/proto/interchain_security/ccv/consumer/v1/query.proto +++ b/proto/interchain_security/ccv/consumer/v1/query.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package interchain_security.ccv.consumer.v1; import "interchain_security/ccv/v1/shared_consumer.proto"; -option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types"; +option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types"; import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; diff --git a/proto/interchain_security/ccv/provider/v1/genesis.proto b/proto/interchain_security/ccv/provider/v1/genesis.proto index 269743721e..443ea26b32 100644 --- a/proto/interchain_security/ccv/provider/v1/genesis.proto +++ b/proto/interchain_security/ccv/provider/v1/genesis.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package interchain_security.ccv.provider.v1; -option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/provider/types"; +option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"; import "gogoproto/gogo.proto"; import "interchain_security/ccv/v1/shared_consumer.proto"; diff --git a/proto/interchain_security/ccv/provider/v1/provider.proto b/proto/interchain_security/ccv/provider/v1/provider.proto index 068cdc0f06..139cc9d25f 100644 --- a/proto/interchain_security/ccv/provider/v1/provider.proto +++ b/proto/interchain_security/ccv/provider/v1/provider.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package interchain_security.ccv.provider.v1; -option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/provider/types"; +option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"; import "interchain_security/ccv/v1/wire.proto"; import "gogoproto/gogo.proto"; diff --git a/proto/interchain_security/ccv/provider/v1/query.proto b/proto/interchain_security/ccv/provider/v1/query.proto index f6d5d5a379..139a7386de 100644 --- a/proto/interchain_security/ccv/provider/v1/query.proto +++ b/proto/interchain_security/ccv/provider/v1/query.proto @@ -1,7 +1,7 @@ syntax = "proto3"; package interchain_security.ccv.provider.v1; -option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/provider/types"; +option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"; import "google/api/annotations.proto"; import "gogoproto/gogo.proto"; diff --git a/proto/interchain_security/ccv/provider/v1/tx.proto b/proto/interchain_security/ccv/provider/v1/tx.proto index 02a69d25f9..8e8aab5408 100644 --- a/proto/interchain_security/ccv/provider/v1/tx.proto +++ b/proto/interchain_security/ccv/provider/v1/tx.proto @@ -1,7 +1,7 @@ syntax = "proto3"; package interchain_security.ccv.provider.v1; -option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/provider/types"; +option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"; import "gogoproto/gogo.proto"; import "cosmos_proto/cosmos.proto"; diff --git a/proto/interchain_security/ccv/v1/shared_consumer.proto b/proto/interchain_security/ccv/v1/shared_consumer.proto index ce65df04a6..d1f0a5d5a3 100644 --- a/proto/interchain_security/ccv/v1/shared_consumer.proto +++ b/proto/interchain_security/ccv/v1/shared_consumer.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package interchain_security.ccv.v1; -option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/types"; +option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/types"; import "tendermint/abci/types.proto"; import "ibc/lightclients/tendermint/v1/tendermint.proto"; diff --git a/proto/interchain_security/ccv/v1/wire.proto b/proto/interchain_security/ccv/v1/wire.proto index f0ba6ab41a..7382b9d0da 100644 --- a/proto/interchain_security/ccv/v1/wire.proto +++ b/proto/interchain_security/ccv/v1/wire.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package interchain_security.ccv.v1; -option go_package = "github.com/cosmos/interchain-security/v5/x/ccv/types"; +option go_package = "github.com/cosmos/interchain-security/v4/x/ccv/types"; import "cosmos/staking/v1beta1/staking.proto"; diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh index fdf6a894c1..67ae158143 100644 --- a/scripts/protocgen.sh +++ b/scripts/protocgen.sh @@ -16,6 +16,6 @@ done cd .. # move proto files to the right places -cp -r github.com/cosmos/interchain-security/v5/* ./ +cp -r github.com/cosmos/interchain-security/v4/* ./ rm -rf github.com diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 31813efd27..1587608177 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -19,9 +19,9 @@ import ( "github.com/tidwall/gjson" "golang.org/x/mod/semver" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/client" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/client" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) const ( diff --git a/tests/integration/common.go b/tests/integration/common.go index 7c4e88c344..18b657ae56 100644 --- a/tests/integration/common.go +++ b/tests/integration/common.go @@ -22,10 +22,10 @@ import ( abci "github.com/cometbft/cometbft/abci/types" tmtypes "github.com/cometbft/cometbft/types" - icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" - testutil "github.com/cosmos/interchain-security/v5/testutil/integration" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + icstestingutils "github.com/cosmos/interchain-security/v4/testutil/ibc_testing" + testutil "github.com/cosmos/interchain-security/v4/testutil/integration" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // ChainType defines the type of chain (either provider or consumer) diff --git a/tests/integration/democracy.go b/tests/integration/democracy.go index 53333aba0e..bbada900a8 100644 --- a/tests/integration/democracy.go +++ b/tests/integration/democracy.go @@ -14,9 +14,9 @@ import ( govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" - icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" - testutil "github.com/cosmos/interchain-security/v5/testutil/integration" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + icstestingutils "github.com/cosmos/interchain-security/v4/testutil/ibc_testing" + testutil "github.com/cosmos/interchain-security/v4/testutil/integration" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" ) type ConsumerDemocracyTestSuite struct { diff --git a/tests/integration/distribution.go b/tests/integration/distribution.go index edd7e3e0e1..5d62b2ee35 100644 --- a/tests/integration/distribution.go +++ b/tests/integration/distribution.go @@ -15,12 +15,12 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - icstestingutils "github.com/cosmos/interchain-security/v5/testutil/integration" - consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + icstestingutils "github.com/cosmos/interchain-security/v4/testutil/integration" + consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // This test is valid for minimal viable consumer chain diff --git a/tests/integration/double_vote.go b/tests/integration/double_vote.go index c23d3feb06..c22ba3b063 100644 --- a/tests/integration/double_vote.go +++ b/tests/integration/double_vote.go @@ -7,8 +7,8 @@ import ( tmcrypto "github.com/cometbft/cometbft/crypto" tmtypes "github.com/cometbft/cometbft/types" - testutil "github.com/cosmos/interchain-security/v5/testutil/crypto" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + testutil "github.com/cosmos/interchain-security/v4/testutil/crypto" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) // TestHandleConsumerDoubleVoting verifies that handling a double voting evidence diff --git a/tests/integration/expired_client.go b/tests/integration/expired_client.go index d5405df1c2..a46df32d8e 100644 --- a/tests/integration/expired_client.go +++ b/tests/integration/expired_client.go @@ -14,7 +14,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // TestVSCPacketSendWithExpiredClient tests queueing of VSCPackets when the consumer client is expired. diff --git a/tests/integration/instance_test.go b/tests/integration/instance_test.go index 424c6c85f5..d2896ad964 100644 --- a/tests/integration/instance_test.go +++ b/tests/integration/instance_test.go @@ -5,11 +5,11 @@ import ( "github.com/stretchr/testify/suite" - appConsumer "github.com/cosmos/interchain-security/v5/app/consumer" - appConsumerDemocracy "github.com/cosmos/interchain-security/v5/app/consumer-democracy" - appProvider "github.com/cosmos/interchain-security/v5/app/provider" - intg "github.com/cosmos/interchain-security/v5/tests/integration" - icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" + appConsumer "github.com/cosmos/interchain-security/v4/app/consumer" + appConsumerDemocracy "github.com/cosmos/interchain-security/v4/app/consumer-democracy" + appProvider "github.com/cosmos/interchain-security/v4/app/provider" + intg "github.com/cosmos/interchain-security/v4/tests/integration" + icstestingutils "github.com/cosmos/interchain-security/v4/testutil/ibc_testing" ) // This file can be used as an example integration testing instance for any provider/consumer applications. diff --git a/tests/integration/key_assignment.go b/tests/integration/key_assignment.go index ad7a1a142e..799109c0d4 100644 --- a/tests/integration/key_assignment.go +++ b/tests/integration/key_assignment.go @@ -9,8 +9,8 @@ import ( tmencoding "github.com/cometbft/cometbft/crypto/encoding" tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) func (s *CCVTestSuite) TestKeyAssignment() { diff --git a/tests/integration/misbehaviour.go b/tests/integration/misbehaviour.go index cea0d15e5f..acdcd0071c 100644 --- a/tests/integration/misbehaviour.go +++ b/tests/integration/misbehaviour.go @@ -9,8 +9,8 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - testutil "github.com/cosmos/interchain-security/v5/testutil/crypto" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + testutil "github.com/cosmos/interchain-security/v4/testutil/crypto" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) // TestHandleConsumerMisbehaviour tests that handling a valid misbehaviour, diff --git a/tests/integration/normal_operations.go b/tests/integration/normal_operations.go index 61f7507793..81c742eeed 100644 --- a/tests/integration/normal_operations.go +++ b/tests/integration/normal_operations.go @@ -5,8 +5,8 @@ import ( tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // Tests the tracking of historical info in the context of new blocks being committed diff --git a/tests/integration/setup.go b/tests/integration/setup.go index a70dab3dec..cb7c78a3be 100644 --- a/tests/integration/setup.go +++ b/tests/integration/setup.go @@ -20,12 +20,12 @@ import ( abci "github.com/cometbft/cometbft/abci/types" tmencoding "github.com/cometbft/cometbft/crypto/encoding" - icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" - testutil "github.com/cosmos/interchain-security/v5/testutil/integration" - "github.com/cosmos/interchain-security/v5/testutil/simibc" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + icstestingutils "github.com/cosmos/interchain-security/v4/testutil/ibc_testing" + testutil "github.com/cosmos/interchain-security/v4/testutil/integration" + "github.com/cosmos/interchain-security/v4/testutil/simibc" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // Callback for instantiating a new coordinator with a provider test chains diff --git a/tests/integration/slashing.go b/tests/integration/slashing.go index a9dcf223f2..19805a49ad 100644 --- a/tests/integration/slashing.go +++ b/tests/integration/slashing.go @@ -18,9 +18,9 @@ import ( "github.com/cometbft/cometbft/crypto/ed25519" tmtypes "github.com/cometbft/cometbft/types" - keepertestutil "github.com/cosmos/interchain-security/v5/testutil/keeper" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + keepertestutil "github.com/cosmos/interchain-security/v4/testutil/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // TestRelayAndApplyDowntimePacket tests that downtime slash packets can be properly relayed diff --git a/tests/integration/soft_opt_out.go b/tests/integration/soft_opt_out.go index 3799de79fc..bce2e1d77c 100644 --- a/tests/integration/soft_opt_out.go +++ b/tests/integration/soft_opt_out.go @@ -10,8 +10,8 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - consumerKeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + consumerKeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // TestSoftOptOut tests the soft opt-out feature diff --git a/tests/integration/stop_consumer.go b/tests/integration/stop_consumer.go index 33f36fa186..6e72679ccd 100644 --- a/tests/integration/stop_consumer.go +++ b/tests/integration/stop_consumer.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // Tests the functionality of stopping a consumer chain at a higher level than unit tests diff --git a/tests/integration/throttle.go b/tests/integration/throttle.go index e9e013575c..ff2d32dade 100644 --- a/tests/integration/throttle.go +++ b/tests/integration/throttle.go @@ -10,10 +10,10 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" - "github.com/cosmos/interchain-security/v5/x/ccv/provider" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + icstestingutils "github.com/cosmos/interchain-security/v4/testutil/ibc_testing" + "github.com/cosmos/interchain-security/v4/x/ccv/provider" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) const fullSlashMeterString = "1.0" diff --git a/tests/integration/throttle_retry.go b/tests/integration/throttle_retry.go index 3d26847136..36acad3602 100644 --- a/tests/integration/throttle_retry.go +++ b/tests/integration/throttle_retry.go @@ -7,7 +7,7 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // TestSlashRetries tests the throttling v2 retry logic at an integration level. diff --git a/tests/integration/unbonding.go b/tests/integration/unbonding.go index ca94fd3679..7f87516444 100644 --- a/tests/integration/unbonding.go +++ b/tests/integration/unbonding.go @@ -7,8 +7,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // TestUndelegationNormalOperation tests that undelegations complete after diff --git a/tests/integration/valset_update.go b/tests/integration/valset_update.go index 113dcc01f4..eb0560a35e 100644 --- a/tests/integration/valset_update.go +++ b/tests/integration/valset_update.go @@ -10,7 +10,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // TestPacketRoundtrip tests a CCV packet roundtrip when tokens are bonded on provider diff --git a/tests/mbt/driver/core.go b/tests/mbt/driver/core.go index 9235071d3e..3c985cb6fa 100644 --- a/tests/mbt/driver/core.go +++ b/tests/mbt/driver/core.go @@ -24,14 +24,14 @@ import ( "github.com/cometbft/cometbft/proto/tendermint/crypto" cmttypes "github.com/cometbft/cometbft/types" - appConsumer "github.com/cosmos/interchain-security/v5/app/consumer" - appProvider "github.com/cosmos/interchain-security/v5/app/provider" - simibc "github.com/cosmos/interchain-security/v5/testutil/simibc" - consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - "github.com/cosmos/interchain-security/v5/x/ccv/types" + appConsumer "github.com/cosmos/interchain-security/v4/app/consumer" + appProvider "github.com/cosmos/interchain-security/v4/app/provider" + simibc "github.com/cosmos/interchain-security/v4/testutil/simibc" + consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // Define a new type for ChainIds to be more explicit diff --git a/tests/mbt/driver/mbt_test.go b/tests/mbt/driver/mbt_test.go index e9a50f5c52..9a82bd7b9b 100644 --- a/tests/mbt/driver/mbt_test.go +++ b/tests/mbt/driver/mbt_test.go @@ -21,9 +21,9 @@ import ( tmencoding "github.com/cometbft/cometbft/crypto/encoding" cmttypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v5/testutil/integration" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/testutil/integration" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v4/x/ccv/types" ) const verbose = false diff --git a/tests/mbt/driver/setup.go b/tests/mbt/driver/setup.go index d30b85bf30..a49a1ac000 100644 --- a/tests/mbt/driver/setup.go +++ b/tests/mbt/driver/setup.go @@ -30,12 +30,12 @@ import ( cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" cmttypes "github.com/cometbft/cometbft/types" - icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" - "github.com/cosmos/interchain-security/v5/testutil/integration" - simibc "github.com/cosmos/interchain-security/v5/testutil/simibc" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + icstestingutils "github.com/cosmos/interchain-security/v4/testutil/ibc_testing" + "github.com/cosmos/interchain-security/v4/testutil/integration" + simibc "github.com/cosmos/interchain-security/v4/testutil/simibc" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) const ( diff --git a/testutil/crypto/crypto.go b/testutil/crypto/crypto.go index df37e99eb5..a9c5341947 100644 --- a/testutil/crypto/crypto.go +++ b/testutil/crypto/crypto.go @@ -15,7 +15,7 @@ import ( tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" tmtypes "github.com/cometbft/cometbft/types" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) // CryptoIdentity is a test helper for generating keys and addresses of diff --git a/testutil/ibc_testing/generic_setup.go b/testutil/ibc_testing/generic_setup.go index 9d5ec02a71..09dfa62140 100644 --- a/testutil/ibc_testing/generic_setup.go +++ b/testutil/ibc_testing/generic_setup.go @@ -16,10 +16,10 @@ import ( tmencoding "github.com/cometbft/cometbft/crypto/encoding" tmtypes "github.com/cometbft/cometbft/types" - testutil "github.com/cosmos/interchain-security/v5/testutil/integration" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + testutil "github.com/cosmos/interchain-security/v4/testutil/integration" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) type ( diff --git a/testutil/ibc_testing/specific_setup.go b/testutil/ibc_testing/specific_setup.go index 81788e10bc..2571346ca6 100644 --- a/testutil/ibc_testing/specific_setup.go +++ b/testutil/ibc_testing/specific_setup.go @@ -17,11 +17,11 @@ import ( "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/log" - appConsumer "github.com/cosmos/interchain-security/v5/app/consumer" - appConsumerDemocracy "github.com/cosmos/interchain-security/v5/app/consumer-democracy" - appProvider "github.com/cosmos/interchain-security/v5/app/provider" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + appConsumer "github.com/cosmos/interchain-security/v4/app/consumer" + appConsumerDemocracy "github.com/cosmos/interchain-security/v4/app/consumer-democracy" + appProvider "github.com/cosmos/interchain-security/v4/app/provider" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) var ( diff --git a/testutil/integration/debug_test.go b/testutil/integration/debug_test.go index ed0ec3d65b..2481e865ab 100644 --- a/testutil/integration/debug_test.go +++ b/testutil/integration/debug_test.go @@ -6,11 +6,11 @@ import ( "reflect" "testing" - appConsumer "github.com/cosmos/interchain-security/v5/app/consumer" - appConsumerDemocracy "github.com/cosmos/interchain-security/v5/app/consumer-democracy" - appProvider "github.com/cosmos/interchain-security/v5/app/provider" - integr "github.com/cosmos/interchain-security/v5/tests/integration" - icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" + appConsumer "github.com/cosmos/interchain-security/v4/app/consumer" + appConsumerDemocracy "github.com/cosmos/interchain-security/v4/app/consumer-democracy" + appProvider "github.com/cosmos/interchain-security/v4/app/provider" + integr "github.com/cosmos/interchain-security/v4/tests/integration" + icstestingutils "github.com/cosmos/interchain-security/v4/testutil/ibc_testing" ) // runCCVTestByName runs a single CCV integration test by name, using a CCVTestSuite diff --git a/testutil/integration/interfaces.go b/testutil/integration/interfaces.go index 83d071a037..fe3382b524 100644 --- a/testutil/integration/interfaces.go +++ b/testutil/integration/interfaces.go @@ -20,9 +20,9 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" - providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // The interface that any provider app must implement to be compatible with ccv integration tests. diff --git a/testutil/keeper/expectations.go b/testutil/keeper/expectations.go index c595a5c080..5a37f3a164 100644 --- a/testutil/keeper/expectations.go +++ b/testutil/keeper/expectations.go @@ -17,8 +17,8 @@ import ( capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - "github.com/cosmos/interchain-security/v5/x/ccv/types" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // diff --git a/testutil/keeper/unit_test_helpers.go b/testutil/keeper/unit_test_helpers.go index 3a80eecce8..abba1a23c9 100644 --- a/testutil/keeper/unit_test_helpers.go +++ b/testutil/keeper/unit_test_helpers.go @@ -27,11 +27,11 @@ import ( "github.com/cometbft/cometbft/libs/log" tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - "github.com/cosmos/interchain-security/v5/x/ccv/types" + consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // Parameters needed to instantiate an in-memory keeper diff --git a/x/ccv/consumer/client/cli/query.go b/x/ccv/consumer/client/cli/query.go index 3630b3ed3f..806b91d2cf 100644 --- a/x/ccv/consumer/client/cli/query.go +++ b/x/ccv/consumer/client/cli/query.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" ) // NewQueryCmd returns a root CLI command handler for all x/ccv/provider query commands. diff --git a/x/ccv/consumer/ibc_module.go b/x/ccv/consumer/ibc_module.go index 4780a0bc73..71caa4a082 100644 --- a/x/ccv/consumer/ibc_module.go +++ b/x/ccv/consumer/ibc_module.go @@ -17,9 +17,9 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // OnChanOpenInit implements the IBCModule interface diff --git a/x/ccv/consumer/ibc_module_test.go b/x/ccv/consumer/ibc_module_test.go index b8868038c9..51fd910902 100644 --- a/x/ccv/consumer/ibc_module_test.go +++ b/x/ccv/consumer/ibc_module_test.go @@ -13,10 +13,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer" - consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer" + consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // TestOnChanOpenInit validates the consumer's OnChanOpenInit implementation against the spec. diff --git a/x/ccv/consumer/keeper/changeover_test.go b/x/ccv/consumer/keeper/changeover_test.go index 89d45534dd..c431f43477 100644 --- a/x/ccv/consumer/keeper/changeover_test.go +++ b/x/ccv/consumer/keeper/changeover_test.go @@ -11,8 +11,8 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v5/testutil/crypto" - uthelpers "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v4/testutil/crypto" + uthelpers "github.com/cosmos/interchain-security/v4/testutil/keeper" ) func TestChangeoverToConsumer(t *testing.T) { diff --git a/x/ccv/consumer/keeper/distribution.go b/x/ccv/consumer/keeper/distribution.go index d6157e50b5..1b98638498 100644 --- a/x/ccv/consumer/keeper/distribution.go +++ b/x/ccv/consumer/keeper/distribution.go @@ -12,8 +12,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // EndBlockRD executes EndBlock logic for the Reward Distribution sub-protocol. diff --git a/x/ccv/consumer/keeper/distribution_test.go b/x/ccv/consumer/keeper/distribution_test.go index 199acae5b0..189842a90e 100644 --- a/x/ccv/consumer/keeper/distribution_test.go +++ b/x/ccv/consumer/keeper/distribution_test.go @@ -10,9 +10,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authTypes "github.com/cosmos/cosmos-sdk/x/auth/types" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // TestGetEstimatedNextFeeDistribution tests next fee distribution parameters. diff --git a/x/ccv/consumer/keeper/genesis.go b/x/ccv/consumer/keeper/genesis.go index bdb8e35418..1b9afb2a4e 100644 --- a/x/ccv/consumer/keeper/genesis.go +++ b/x/ccv/consumer/keeper/genesis.go @@ -7,8 +7,8 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // InitGenesis initializes the CCV consumer state and binds to PortID. diff --git a/x/ccv/consumer/keeper/genesis_test.go b/x/ccv/consumer/keeper/genesis_test.go index 77e7e639d3..b9fc1b6f3a 100644 --- a/x/ccv/consumer/keeper/genesis_test.go +++ b/x/ccv/consumer/keeper/genesis_test.go @@ -18,11 +18,11 @@ import ( abci "github.com/cometbft/cometbft/abci/types" tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v5/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // TestInitGenesis tests that a consumer chain is correctly initialised from genesis. diff --git a/x/ccv/consumer/keeper/grpc_query.go b/x/ccv/consumer/keeper/grpc_query.go index 2f1543071b..5ac5116a28 100644 --- a/x/ccv/consumer/keeper/grpc_query.go +++ b/x/ccv/consumer/keeper/grpc_query.go @@ -8,8 +8,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) var _ types.QueryServer = Keeper{} //nolint:golint diff --git a/x/ccv/consumer/keeper/hooks.go b/x/ccv/consumer/keeper/hooks.go index 715be50e08..c730bf266c 100644 --- a/x/ccv/consumer/keeper/hooks.go +++ b/x/ccv/consumer/keeper/hooks.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) var _ ccv.ConsumerHooks = Keeper{} diff --git a/x/ccv/consumer/keeper/keeper.go b/x/ccv/consumer/keeper/keeper.go index e4f1830333..b8751344b8 100644 --- a/x/ccv/consumer/keeper/keeper.go +++ b/x/ccv/consumer/keeper/keeper.go @@ -23,8 +23,8 @@ import ( tmtypes "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/log" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // Keeper defines the Cross-Chain Validation Consumer Keeper diff --git a/x/ccv/consumer/keeper/keeper_test.go b/x/ccv/consumer/keeper/keeper_test.go index 4ece86571d..19856211b5 100644 --- a/x/ccv/consumer/keeper/keeper_test.go +++ b/x/ccv/consumer/keeper/keeper_test.go @@ -17,10 +17,10 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v5/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // TestProviderClientID tests getter and setter functionality for the client ID stored on consumer keeper diff --git a/x/ccv/consumer/keeper/migrations.go b/x/ccv/consumer/keeper/migrations.go index 71139fb36c..a1e826e61e 100644 --- a/x/ccv/consumer/keeper/migrations.go +++ b/x/ccv/consumer/keeper/migrations.go @@ -4,7 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - v2 "github.com/cosmos/interchain-security/v5/x/ccv/consumer/migrations/v2" + v2 "github.com/cosmos/interchain-security/v4/x/ccv/consumer/migrations/v2" ) // Migrator is a struct for handling in-place store migrations. diff --git a/x/ccv/consumer/keeper/params.go b/x/ccv/consumer/keeper/params.go index 818b2858bc..4d96ddf604 100644 --- a/x/ccv/consumer/keeper/params.go +++ b/x/ccv/consumer/keeper/params.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // GetParams returns the params for the consumer ccv module diff --git a/x/ccv/consumer/keeper/params_test.go b/x/ccv/consumer/keeper/params_test.go index 18d3f5a2b2..e2975a0b31 100644 --- a/x/ccv/consumer/keeper/params_test.go +++ b/x/ccv/consumer/keeper/params_test.go @@ -6,8 +6,8 @@ import ( "github.com/stretchr/testify/require" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // TestParams tests getters/setters for consumer params diff --git a/x/ccv/consumer/keeper/provider_info.go b/x/ccv/consumer/keeper/provider_info.go index aab84e1f85..e5fbaf6540 100644 --- a/x/ccv/consumer/keeper/provider_info.go +++ b/x/ccv/consumer/keeper/provider_info.go @@ -5,8 +5,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) func (k Keeper) GetProviderInfo(ctx sdk.Context) (*types.QueryProviderInfoResponse, error) { //nolint:golint diff --git a/x/ccv/consumer/keeper/relay.go b/x/ccv/consumer/keeper/relay.go index cf431fc054..2d4e16510a 100644 --- a/x/ccv/consumer/keeper/relay.go +++ b/x/ccv/consumer/keeper/relay.go @@ -14,8 +14,8 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // OnRecvVSCPacket sets the pending validator set changes that will be flushed to ABCI on Endblock diff --git a/x/ccv/consumer/keeper/relay_test.go b/x/ccv/consumer/keeper/relay_test.go index b1042de5d0..46a805a85a 100644 --- a/x/ccv/consumer/keeper/relay_test.go +++ b/x/ccv/consumer/keeper/relay_test.go @@ -21,11 +21,11 @@ import ( abci "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/bytes" - "github.com/cosmos/interchain-security/v5/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - consumerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // TestOnRecvVSCPacket tests the behavior of OnRecvVSCPacket over various packet scenarios diff --git a/x/ccv/consumer/keeper/soft_opt_out.go b/x/ccv/consumer/keeper/soft_opt_out.go index 120d3d46d1..e5990ff65d 100644 --- a/x/ccv/consumer/keeper/soft_opt_out.go +++ b/x/ccv/consumer/keeper/soft_opt_out.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" ) // BeginBlockSoftOptOut executes BeginBlock logic for the Soft Opt-Out sub-protocol diff --git a/x/ccv/consumer/keeper/soft_opt_out_test.go b/x/ccv/consumer/keeper/soft_opt_out_test.go index 5c21133832..1a726d1767 100644 --- a/x/ccv/consumer/keeper/soft_opt_out_test.go +++ b/x/ccv/consumer/keeper/soft_opt_out_test.go @@ -7,9 +7,9 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v5/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // Tests that UpdateSmallestNonOptOutPower updates the smallest validator power that cannot soft opt out. diff --git a/x/ccv/consumer/keeper/throttle_retry.go b/x/ccv/consumer/keeper/throttle_retry.go index 4b6df3cc04..22e48f9175 100644 --- a/x/ccv/consumer/keeper/throttle_retry.go +++ b/x/ccv/consumer/keeper/throttle_retry.go @@ -5,7 +5,7 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" ) // diff --git a/x/ccv/consumer/keeper/throttle_retry_test.go b/x/ccv/consumer/keeper/throttle_retry_test.go index b979ebb51d..4a222fde90 100644 --- a/x/ccv/consumer/keeper/throttle_retry_test.go +++ b/x/ccv/consumer/keeper/throttle_retry_test.go @@ -6,9 +6,9 @@ import ( "github.com/stretchr/testify/require" - testutil "github.com/cosmos/interchain-security/v5/testutil/keeper" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + testutil "github.com/cosmos/interchain-security/v4/testutil/keeper" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) func TestPacketSendingPermitted(t *testing.T) { diff --git a/x/ccv/consumer/keeper/validators.go b/x/ccv/consumer/keeper/validators.go index fa1c8991db..24a1c5a57c 100644 --- a/x/ccv/consumer/keeper/validators.go +++ b/x/ccv/consumer/keeper/validators.go @@ -11,7 +11,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" ) // diff --git a/x/ccv/consumer/keeper/validators_test.go b/x/ccv/consumer/keeper/validators_test.go index f1c3dbd3f3..1d4dcb2c86 100644 --- a/x/ccv/consumer/keeper/validators_test.go +++ b/x/ccv/consumer/keeper/validators_test.go @@ -15,10 +15,10 @@ import ( tmrand "github.com/cometbft/cometbft/libs/rand" tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v5/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v4/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" ) // TestApplyCCValidatorChanges tests the ApplyCCValidatorChanges method for a consumer keeper diff --git a/x/ccv/consumer/migrations/v2/migration.go b/x/ccv/consumer/migrations/v2/migration.go index ed54512172..80f06d4d71 100644 --- a/x/ccv/consumer/migrations/v2/migration.go +++ b/x/ccv/consumer/migrations/v2/migration.go @@ -6,8 +6,8 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // MigrateConsumerPacketData migrates consumer packet data according to diff --git a/x/ccv/consumer/migrations/v2/migration_test.go b/x/ccv/consumer/migrations/v2/migration_test.go index 8ef05bcbcf..a99a2be0fb 100644 --- a/x/ccv/consumer/migrations/v2/migration_test.go +++ b/x/ccv/consumer/migrations/v2/migration_test.go @@ -9,10 +9,10 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - testutil "github.com/cosmos/interchain-security/v5/testutil/keeper" - v2 "github.com/cosmos/interchain-security/v5/x/ccv/consumer/migrations/v2" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + testutil "github.com/cosmos/interchain-security/v4/testutil/keeper" + v2 "github.com/cosmos/interchain-security/v4/x/ccv/consumer/migrations/v2" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) func TestMigrateConsumerPacketData(t *testing.T) { diff --git a/x/ccv/consumer/module.go b/x/ccv/consumer/module.go index f522a34fb8..4b5d9c053b 100644 --- a/x/ccv/consumer/module.go +++ b/x/ccv/consumer/module.go @@ -19,10 +19,10 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/client/cli" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/keeper" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/client/cli" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) var ( diff --git a/x/ccv/consumer/types/genesis.go b/x/ccv/consumer/types/genesis.go index 9c3fab9c15..cb9cb61f40 100644 --- a/x/ccv/consumer/types/genesis.go +++ b/x/ccv/consumer/types/genesis.go @@ -7,7 +7,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // NewRestartGenesisState returns a consumer GenesisState that has already been established. diff --git a/x/ccv/consumer/types/genesis.pb.go b/x/ccv/consumer/types/genesis.pb.go index b51d09efac..dbb7c51981 100644 --- a/x/ccv/consumer/types/genesis.pb.go +++ b/x/ccv/consumer/types/genesis.pb.go @@ -10,7 +10,7 @@ import ( proto "github.com/cosmos/gogoproto/proto" github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" _07_tendermint "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" - types "github.com/cosmos/interchain-security/v5/x/ccv/types" + types "github.com/cosmos/interchain-security/v4/x/ccv/types" _ "google.golang.org/protobuf/types/known/timestamppb" io "io" math "math" diff --git a/x/ccv/consumer/types/genesis_test.go b/x/ccv/consumer/types/genesis_test.go index befd935ba4..dcd30b642c 100644 --- a/x/ccv/consumer/types/genesis_test.go +++ b/x/ccv/consumer/types/genesis_test.go @@ -15,9 +15,9 @@ import ( abci "github.com/cometbft/cometbft/abci/types" tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v5/testutil/crypto" - "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/testutil/crypto" + "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) const ( diff --git a/x/ccv/consumer/types/keys.go b/x/ccv/consumer/types/keys.go index f6400266e8..20163f5ed9 100644 --- a/x/ccv/consumer/types/keys.go +++ b/x/ccv/consumer/types/keys.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) const ( diff --git a/x/ccv/consumer/types/params_test.go b/x/ccv/consumer/types/params_test.go index caff4db0b1..5b11b52d43 100644 --- a/x/ccv/consumer/types/params_test.go +++ b/x/ccv/consumer/types/params_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // Tests the validation of consumer params that happens at genesis diff --git a/x/ccv/consumer/types/query.pb.go b/x/ccv/consumer/types/query.pb.go index 29f7d33971..fe62a4217f 100644 --- a/x/ccv/consumer/types/query.pb.go +++ b/x/ccv/consumer/types/query.pb.go @@ -9,7 +9,7 @@ import ( _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" proto "github.com/cosmos/gogoproto/proto" - types "github.com/cosmos/interchain-security/v5/x/ccv/types" + types "github.com/cosmos/interchain-security/v4/x/ccv/types" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" diff --git a/x/ccv/democracy/distribution/module.go b/x/ccv/democracy/distribution/module.go index b30893402b..af35c86846 100644 --- a/x/ccv/democracy/distribution/module.go +++ b/x/ccv/democracy/distribution/module.go @@ -16,7 +16,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" ) var ( diff --git a/x/ccv/provider/client/cli/query.go b/x/ccv/provider/client/cli/query.go index 575470c6f3..cf68d24681 100644 --- a/x/ccv/provider/client/cli/query.go +++ b/x/ccv/provider/client/cli/query.go @@ -11,7 +11,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) // NewQueryCmd returns a root CLI command handler for all x/ccv/provider query commands. diff --git a/x/ccv/provider/client/cli/tx.go b/x/ccv/provider/client/cli/tx.go index ef65abc28e..876aaa9b8b 100644 --- a/x/ccv/provider/client/cli/tx.go +++ b/x/ccv/provider/client/cli/tx.go @@ -18,7 +18,7 @@ import ( tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) // GetTxCmd returns the transaction commands for this module diff --git a/x/ccv/provider/client/proposal_handler.go b/x/ccv/provider/client/proposal_handler.go index 9942a6d431..bb88276393 100644 --- a/x/ccv/provider/client/proposal_handler.go +++ b/x/ccv/provider/client/proposal_handler.go @@ -20,7 +20,7 @@ import ( govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) var ( diff --git a/x/ccv/provider/handler.go b/x/ccv/provider/handler.go index 72abf10c1c..8232ef3b6c 100644 --- a/x/ccv/provider/handler.go +++ b/x/ccv/provider/handler.go @@ -6,8 +6,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) func NewHandler(k *keeper.Keeper) sdk.Handler { diff --git a/x/ccv/provider/handler_test.go b/x/ccv/provider/handler_test.go index 2351b6c61c..6e13511e3b 100644 --- a/x/ccv/provider/handler_test.go +++ b/x/ccv/provider/handler_test.go @@ -14,11 +14,11 @@ import ( tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - testcrypto "github.com/cosmos/interchain-security/v5/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider" - keeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + testcrypto "github.com/cosmos/interchain-security/v4/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider" + keeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) func TestInvalidMsg(t *testing.T) { diff --git a/x/ccv/provider/ibc_middleware.go b/x/ccv/provider/ibc_middleware.go index a1ef6e0db8..fb4d96890f 100644 --- a/x/ccv/provider/ibc_middleware.go +++ b/x/ccv/provider/ibc_middleware.go @@ -12,8 +12,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) var _ porttypes.Middleware = &IBCMiddleware{} diff --git a/x/ccv/provider/ibc_middleware_test.go b/x/ccv/provider/ibc_middleware_test.go index e2be5a649d..347cdc66cb 100644 --- a/x/ccv/provider/ibc_middleware_test.go +++ b/x/ccv/provider/ibc_middleware_test.go @@ -7,7 +7,7 @@ import ( channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" "github.com/stretchr/testify/require" - "github.com/cosmos/interchain-security/v5/x/ccv/provider" + "github.com/cosmos/interchain-security/v4/x/ccv/provider" ) func TestGetProviderDenom(t *testing.T) { diff --git a/x/ccv/provider/ibc_module.go b/x/ccv/provider/ibc_module.go index f4b1eace4d..f6ad43d60b 100644 --- a/x/ccv/provider/ibc_module.go +++ b/x/ccv/provider/ibc_module.go @@ -15,9 +15,9 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // OnChanOpenInit implements the IBCModule interface diff --git a/x/ccv/provider/ibc_module_test.go b/x/ccv/provider/ibc_module_test.go index 9d41a4d9e1..df12ed4cb8 100644 --- a/x/ccv/provider/ibc_module_test.go +++ b/x/ccv/provider/ibc_module_test.go @@ -15,11 +15,11 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider" - providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider" + providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // TestOnChanOpenInit tests the provider's OnChanOpenInit method against spec. diff --git a/x/ccv/provider/keeper/consumer_equivocation.go b/x/ccv/provider/keeper/consumer_equivocation.go index 6324dfa5c0..8fc9808304 100644 --- a/x/ccv/provider/keeper/consumer_equivocation.go +++ b/x/ccv/provider/keeper/consumer_equivocation.go @@ -19,8 +19,8 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // diff --git a/x/ccv/provider/keeper/consumer_equivocation_test.go b/x/ccv/provider/keeper/consumer_equivocation_test.go index 13b56fc04a..d92caf69a3 100644 --- a/x/ccv/provider/keeper/consumer_equivocation_test.go +++ b/x/ccv/provider/keeper/consumer_equivocation_test.go @@ -18,9 +18,9 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) func TestVerifyDoubleVotingEvidence(t *testing.T) { diff --git a/x/ccv/provider/keeper/distribution.go b/x/ccv/provider/keeper/distribution.go index 49137936eb..67e6193e5d 100644 --- a/x/ccv/provider/keeper/distribution.go +++ b/x/ccv/provider/keeper/distribution.go @@ -12,7 +12,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) // BeginBlockRD executes BeginBlock logic for the Reward Distribution sub-protocol. diff --git a/x/ccv/provider/keeper/distribution_test.go b/x/ccv/provider/keeper/distribution_test.go index 4a5cd750fc..d95ae598c0 100644 --- a/x/ccv/provider/keeper/distribution_test.go +++ b/x/ccv/provider/keeper/distribution_test.go @@ -14,9 +14,9 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) func TestComputeConsumerTotalVotingPower(t *testing.T) { diff --git a/x/ccv/provider/keeper/genesis.go b/x/ccv/provider/keeper/genesis.go index 66895233a7..2075ff48ae 100644 --- a/x/ccv/provider/keeper/genesis.go +++ b/x/ccv/provider/keeper/genesis.go @@ -5,8 +5,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // InitGenesis initializes the CCV provider state and binds to PortID. diff --git a/x/ccv/provider/keeper/genesis_test.go b/x/ccv/provider/keeper/genesis_test.go index e75241eb58..81b0a90bd8 100644 --- a/x/ccv/provider/keeper/genesis_test.go +++ b/x/ccv/provider/keeper/genesis_test.go @@ -11,11 +11,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v5/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // TestInitAndExportGenesis tests the export and the initialisation of a provider chain genesis diff --git a/x/ccv/provider/keeper/grpc_query.go b/x/ccv/provider/keeper/grpc_query.go index 2baed949b7..84c5928fea 100644 --- a/x/ccv/provider/keeper/grpc_query.go +++ b/x/ccv/provider/keeper/grpc_query.go @@ -11,8 +11,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) var _ types.QueryServer = Keeper{} diff --git a/x/ccv/provider/keeper/grpc_query_test.go b/x/ccv/provider/keeper/grpc_query_test.go index 8283f11235..14e6e675e1 100644 --- a/x/ccv/provider/keeper/grpc_query_test.go +++ b/x/ccv/provider/keeper/grpc_query_test.go @@ -8,10 +8,10 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" - cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) func TestQueryAllPairsValConAddrByConsumerChainID(t *testing.T) { diff --git a/x/ccv/provider/keeper/hooks.go b/x/ccv/provider/keeper/hooks.go index 763804868e..ed926f16c7 100644 --- a/x/ccv/provider/keeper/hooks.go +++ b/x/ccv/provider/keeper/hooks.go @@ -8,9 +8,9 @@ import ( v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // Wrapper struct diff --git a/x/ccv/provider/keeper/hooks_test.go b/x/ccv/provider/keeper/hooks_test.go index 5372fc37cd..931667fac4 100644 --- a/x/ccv/provider/keeper/hooks_test.go +++ b/x/ccv/provider/keeper/hooks_test.go @@ -15,9 +15,9 @@ import ( v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" ) func TestValidatorConsensusKeyInUse(t *testing.T) { diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index e602c16e5e..bc3664bfd7 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -23,9 +23,9 @@ import ( "github.com/cometbft/cometbft/libs/log" - consumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // Keeper defines the Cross-Chain Validation Provider Keeper diff --git a/x/ccv/provider/keeper/keeper_test.go b/x/ccv/provider/keeper/keeper_test.go index a66b7e3826..d172a96dba 100644 --- a/x/ccv/provider/keeper/keeper_test.go +++ b/x/ccv/provider/keeper/keeper_test.go @@ -16,10 +16,10 @@ import ( abci "github.com/cometbft/cometbft/abci/types" tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) const consumer = "consumer" diff --git a/x/ccv/provider/keeper/key_assignment.go b/x/ccv/provider/keeper/key_assignment.go index cb359620b9..67f4d89fdd 100644 --- a/x/ccv/provider/keeper/key_assignment.go +++ b/x/ccv/provider/keeper/key_assignment.go @@ -11,8 +11,8 @@ import ( tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // ParseConsumerKey parses the ED25519 PubKey`consumerKey` from a JSON string diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index e721b39c33..abf7858d40 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -17,11 +17,11 @@ import ( abci "github.com/cometbft/cometbft/abci/types" tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) const ChainID = "chainID" diff --git a/x/ccv/provider/keeper/msg_server.go b/x/ccv/provider/keeper/msg_server.go index ee17fd8228..1f7f51b589 100644 --- a/x/ccv/provider/keeper/msg_server.go +++ b/x/ccv/provider/keeper/msg_server.go @@ -11,8 +11,8 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) type msgServer struct { diff --git a/x/ccv/provider/keeper/params.go b/x/ccv/provider/keeper/params.go index 3834358b0b..f74baf656e 100644 --- a/x/ccv/provider/keeper/params.go +++ b/x/ccv/provider/keeper/params.go @@ -7,8 +7,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // GetTemplateClient returns the template client for provider proposals diff --git a/x/ccv/provider/keeper/params_test.go b/x/ccv/provider/keeper/params_test.go index ccbd8629d6..88175431c0 100644 --- a/x/ccv/provider/keeper/params_test.go +++ b/x/ccv/provider/keeper/params_test.go @@ -11,8 +11,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) // TestParams tests the getting/setting of provider ccv module params. diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index 9020a3db82..0d5177089a 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -9,7 +9,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) // HandleOptIn prepares validator `providerAddr` to opt in to `chainID` with an optional `consumerKey` consumer public key. diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index 42ff7a14ab..afbd420e7f 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -17,10 +17,10 @@ import ( "github.com/cometbft/cometbft/proto/tendermint/crypto" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) func TestHandleOptIn(t *testing.T) { diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 67d3c91bca..1d2bf19a4f 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -18,8 +18,8 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // HandleConsumerAdditionProposal will receive the consumer chain's client state from the proposal. diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index 67b0f93778..9b769d2f94 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -17,11 +17,11 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index f785d5d6b5..230ed0a96a 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -12,8 +12,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // OnRecvVSCMaturedPacket handles a VSCMatured packet and returns a no-op result ack. diff --git a/x/ccv/provider/keeper/relay_test.go b/x/ccv/provider/keeper/relay_test.go index 2b50c695bc..5f74d93424 100644 --- a/x/ccv/provider/keeper/relay_test.go +++ b/x/ccv/provider/keeper/relay_test.go @@ -20,13 +20,13 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v5/testutil/crypto" - cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/testutil/crypto" + cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // TestQueueVSCPackets tests queueing validator set updates. diff --git a/x/ccv/provider/keeper/throttle.go b/x/ccv/provider/keeper/throttle.go index b1da78db0f..b7e7fd5941 100644 --- a/x/ccv/provider/keeper/throttle.go +++ b/x/ccv/provider/keeper/throttle.go @@ -10,7 +10,7 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) // Obtains the effective validator power relevant to a validator consensus address. diff --git a/x/ccv/provider/keeper/throttle_legacy.go b/x/ccv/provider/keeper/throttle_legacy.go index c14c994e84..6f347b8f60 100644 --- a/x/ccv/provider/keeper/throttle_legacy.go +++ b/x/ccv/provider/keeper/throttle_legacy.go @@ -5,8 +5,8 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // Pending packet data type enum, used to encode the type of packet data stored at each entry in the mutual queue. diff --git a/x/ccv/provider/keeper/throttle_test.go b/x/ccv/provider/keeper/throttle_test.go index 5a315e5ec9..478ec0f235 100644 --- a/x/ccv/provider/keeper/throttle_test.go +++ b/x/ccv/provider/keeper/throttle_test.go @@ -13,8 +13,8 @@ import ( tmtypes "github.com/cometbft/cometbft/types" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) // TestSlashMeterReplenishment tests the CheckForSlashMeterReplenishment, ReplenishSlashMeter, diff --git a/x/ccv/provider/keeper/validator_set_update.go b/x/ccv/provider/keeper/validator_set_update.go index 42016708a5..264fdd67f2 100644 --- a/x/ccv/provider/keeper/validator_set_update.go +++ b/x/ccv/provider/keeper/validator_set_update.go @@ -8,7 +8,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) // SetConsumerValidator sets provided consumer `validator` on the consumer chain with `chainID` diff --git a/x/ccv/provider/keeper/validator_set_update_test.go b/x/ccv/provider/keeper/validator_set_update_test.go index 9da3039b14..396a74fb1f 100644 --- a/x/ccv/provider/keeper/validator_set_update_test.go +++ b/x/ccv/provider/keeper/validator_set_update_test.go @@ -16,10 +16,10 @@ import ( "github.com/cometbft/cometbft/crypto/ed25519" "github.com/cometbft/cometbft/proto/tendermint/crypto" - cryptotestutil "github.com/cosmos/interchain-security/v5/testutil/crypto" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + cryptotestutil "github.com/cosmos/interchain-security/v4/testutil/crypto" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) // TestConsumerValidator tests the `SetConsumerValidator`, `IsConsumerValidator`, and `DeleteConsumerValidator` methods diff --git a/x/ccv/provider/migrations/migrator.go b/x/ccv/provider/migrations/migrator.go index ea6e9e00d5..c8b464a7a3 100644 --- a/x/ccv/provider/migrations/migrator.go +++ b/x/ccv/provider/migrations/migrator.go @@ -4,10 +4,10 @@ import ( sdktypes "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - v3 "github.com/cosmos/interchain-security/v5/x/ccv/provider/migrations/v3" - v4 "github.com/cosmos/interchain-security/v5/x/ccv/provider/migrations/v4" - v5 "github.com/cosmos/interchain-security/v5/x/ccv/provider/migrations/v5" + providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + v3 "github.com/cosmos/interchain-security/v4/x/ccv/provider/migrations/v3" + v4 "github.com/cosmos/interchain-security/v4/x/ccv/provider/migrations/v4" + v5 "github.com/cosmos/interchain-security/v4/x/ccv/provider/migrations/v5" ) // Migrator is a struct for handling in-place store migrations. @@ -41,9 +41,9 @@ func (m Migrator) Migrate3to4(ctx sdktypes.Context) error { return nil } -// MigrateXtoY migrates x/ccvprovider state from consensus version X to Y. +// MigrateXtoY migrates x/ccvprovider state from consensus version 4 to 5. // The migration consists of setting a top N of 95 for all registered consumer chains. -func (m Migrator) MigrateXtoY(ctx sdktypes.Context) error { +func (m Migrator) Migrate4to5(ctx sdktypes.Context) error { v5.MigrateTopNForRegisteredChains(ctx, m.providerKeeper) return nil } diff --git a/x/ccv/provider/migrations/v3/migration_test.go b/x/ccv/provider/migrations/v3/migration_test.go index 56d6b617d9..630b8fd7dd 100644 --- a/x/ccv/provider/migrations/v3/migration_test.go +++ b/x/ccv/provider/migrations/v3/migration_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - testutil "github.com/cosmos/interchain-security/v5/testutil/keeper" + testutil "github.com/cosmos/interchain-security/v4/testutil/keeper" ) func TestMigrate2To3(t *testing.T) { diff --git a/x/ccv/provider/migrations/v3/migrations.go b/x/ccv/provider/migrations/v3/migrations.go index 2ffd1e6f25..d308316761 100644 --- a/x/ccv/provider/migrations/v3/migrations.go +++ b/x/ccv/provider/migrations/v3/migrations.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" ) // MigrateQueuedPackets processes all queued packet data for all consumer chains that were stored diff --git a/x/ccv/provider/migrations/v4/migration_test.go b/x/ccv/provider/migrations/v4/migration_test.go index 4423842149..5ab5faf87a 100644 --- a/x/ccv/provider/migrations/v4/migration_test.go +++ b/x/ccv/provider/migrations/v4/migration_test.go @@ -5,8 +5,8 @@ import ( "github.com/stretchr/testify/require" - testutil "github.com/cosmos/interchain-security/v5/testutil/keeper" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + testutil "github.com/cosmos/interchain-security/v4/testutil/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) func TestMigrateParams(t *testing.T) { diff --git a/x/ccv/provider/migrations/v4/migrations.go b/x/ccv/provider/migrations/v4/migrations.go index e60c98700e..825d01e25d 100644 --- a/x/ccv/provider/migrations/v4/migrations.go +++ b/x/ccv/provider/migrations/v4/migrations.go @@ -4,7 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) // MigrateParams adds missing provider chain params to the param store. diff --git a/x/ccv/provider/migrations/v5/migration_test.go b/x/ccv/provider/migrations/v5/migration_test.go index 907aa1b019..1adf57baeb 100644 --- a/x/ccv/provider/migrations/v5/migration_test.go +++ b/x/ccv/provider/migrations/v5/migration_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - testutil "github.com/cosmos/interchain-security/v5/testutil/keeper" + testutil "github.com/cosmos/interchain-security/v4/testutil/keeper" ) func TestMigrateParams(t *testing.T) { diff --git a/x/ccv/provider/migrations/v5/migrations.go b/x/ccv/provider/migrations/v5/migrations.go index aa228b6a09..748b35dd0e 100644 --- a/x/ccv/provider/migrations/v5/migrations.go +++ b/x/ccv/provider/migrations/v5/migrations.go @@ -3,7 +3,7 @@ package v5 import ( sdk "github.com/cosmos/cosmos-sdk/types" - providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" ) // This migration only takes already registered chains into account. diff --git a/x/ccv/provider/module.go b/x/ccv/provider/module.go index 85555af2c2..6b616f0de8 100644 --- a/x/ccv/provider/module.go +++ b/x/ccv/provider/module.go @@ -19,10 +19,10 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/client/cli" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/migrations" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/client/cli" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/migrations" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) var ( @@ -115,6 +115,9 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { if err := cfg.RegisterMigration(providertypes.ModuleName, 3, m.Migrate3to4); err != nil { panic(fmt.Sprintf("failed to register migrator for %s: %s", providertypes.ModuleName, err)) } + if err := cfg.RegisterMigration(providertypes.ModuleName, 4, m.Migrate4to5); err != nil { + panic(fmt.Sprintf("failed to register migrator for %s: %s", providertypes.ModuleName, err)) + } } // InitGenesis performs genesis initialization for the provider module. It returns no validator updates. diff --git a/x/ccv/provider/module_test.go b/x/ccv/provider/module_test.go index 2dfdfc1598..869c24253f 100644 --- a/x/ccv/provider/module_test.go +++ b/x/ccv/provider/module_test.go @@ -11,10 +11,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // Tests the provider's InitGenesis implementation against the spec. diff --git a/x/ccv/provider/proposal_handler.go b/x/ccv/provider/proposal_handler.go index 2a4342ea20..50089a8ab5 100644 --- a/x/ccv/provider/proposal_handler.go +++ b/x/ccv/provider/proposal_handler.go @@ -7,8 +7,8 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) // NewProviderProposalHandler defines the handler for consumer addition, diff --git a/x/ccv/provider/proposal_handler_test.go b/x/ccv/provider/proposal_handler_test.go index e15036f565..185db25b07 100644 --- a/x/ccv/provider/proposal_handler_test.go +++ b/x/ccv/provider/proposal_handler_test.go @@ -12,9 +12,9 @@ import ( distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" - "github.com/cosmos/interchain-security/v5/x/ccv/provider" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" + "github.com/cosmos/interchain-security/v4/x/ccv/provider" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) // TestProviderProposalHandler tests the highest level handler for proposals diff --git a/x/ccv/provider/types/consumer.go b/x/ccv/provider/types/consumer.go index 02651ac03e..4c43bd58e7 100644 --- a/x/ccv/provider/types/consumer.go +++ b/x/ccv/provider/types/consumer.go @@ -1,7 +1,7 @@ package types import ( - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) func NewConsumerStates( diff --git a/x/ccv/provider/types/genesis.go b/x/ccv/provider/types/genesis.go index 00802558c5..ae929ba541 100644 --- a/x/ccv/provider/types/genesis.go +++ b/x/ccv/provider/types/genesis.go @@ -9,7 +9,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) func NewGenesisState( diff --git a/x/ccv/provider/types/genesis.pb.go b/x/ccv/provider/types/genesis.pb.go index d7d225b762..d5041f1b10 100644 --- a/x/ccv/provider/types/genesis.pb.go +++ b/x/ccv/provider/types/genesis.pb.go @@ -7,7 +7,7 @@ import ( fmt "fmt" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" - types "github.com/cosmos/interchain-security/v5/x/ccv/types" + types "github.com/cosmos/interchain-security/v4/x/ccv/types" io "io" math "math" math_bits "math/bits" diff --git a/x/ccv/provider/types/genesis_test.go b/x/ccv/provider/types/genesis_test.go index d682e93897..41a716757f 100644 --- a/x/ccv/provider/types/genesis_test.go +++ b/x/ccv/provider/types/genesis_test.go @@ -14,9 +14,9 @@ import ( abci "github.com/cometbft/cometbft/abci/types" tmtypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/interchain-security/v5/testutil/crypto" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" - ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/testutil/crypto" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // Tests validation of consumer states and params within a provider genesis state diff --git a/x/ccv/provider/types/key_assignment.go b/x/ccv/provider/types/key_assignment.go index ee403e1e49..04192ae53a 100644 --- a/x/ccv/provider/types/key_assignment.go +++ b/x/ccv/provider/types/key_assignment.go @@ -8,7 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // A validator's consensus address on the provider chain. diff --git a/x/ccv/provider/types/keys.go b/x/ccv/provider/types/keys.go index 4126a58cb7..14521b9289 100644 --- a/x/ccv/provider/types/keys.go +++ b/x/ccv/provider/types/keys.go @@ -8,7 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) type Status int diff --git a/x/ccv/provider/types/keys_test.go b/x/ccv/provider/types/keys_test.go index 78d8b4f806..eac11a0993 100644 --- a/x/ccv/provider/types/keys_test.go +++ b/x/ccv/provider/types/keys_test.go @@ -8,8 +8,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - cryptoutil "github.com/cosmos/interchain-security/v5/testutil/crypto" - providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + cryptoutil "github.com/cosmos/interchain-security/v4/testutil/crypto" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) // Tests that all singular keys, or prefixes to fully resolves keys are non duplicate byte values. diff --git a/x/ccv/provider/types/msg.go b/x/ccv/provider/types/msg.go index b6546506fd..b80e608785 100644 --- a/x/ccv/provider/types/msg.go +++ b/x/ccv/provider/types/msg.go @@ -14,7 +14,7 @@ import ( tmtypes "github.com/cometbft/cometbft/proto/tendermint/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // provider message types diff --git a/x/ccv/provider/types/params.go b/x/ccv/provider/types/params.go index c398188c98..a0a7a5ed7a 100644 --- a/x/ccv/provider/types/params.go +++ b/x/ccv/provider/types/params.go @@ -11,7 +11,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) const ( diff --git a/x/ccv/provider/types/params_test.go b/x/ccv/provider/types/params_test.go index b86dd0cddf..4e72c233af 100644 --- a/x/ccv/provider/types/params_test.go +++ b/x/ccv/provider/types/params_test.go @@ -11,7 +11,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) func TestValidateParams(t *testing.T) { diff --git a/x/ccv/provider/types/proposal.go b/x/ccv/provider/types/proposal.go index d2cfa7ed3f..fa6483acd1 100644 --- a/x/ccv/provider/types/proposal.go +++ b/x/ccv/provider/types/proposal.go @@ -14,7 +14,7 @@ import ( evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" + ccvtypes "github.com/cosmos/interchain-security/v4/x/ccv/types" ) const ( diff --git a/x/ccv/provider/types/proposal_test.go b/x/ccv/provider/types/proposal_test.go index a2f8f574ee..465a376b47 100644 --- a/x/ccv/provider/types/proposal_test.go +++ b/x/ccv/provider/types/proposal_test.go @@ -15,7 +15,7 @@ import ( govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) func TestConsumerAdditionProposalValidateBasic(t *testing.T) { diff --git a/x/ccv/provider/types/provider.pb.go b/x/ccv/provider/types/provider.pb.go index 07e511669c..4f0a2fd605 100644 --- a/x/ccv/provider/types/provider.pb.go +++ b/x/ccv/provider/types/provider.pb.go @@ -15,7 +15,7 @@ import ( github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" types "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" _07_tendermint "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" - types3 "github.com/cosmos/interchain-security/v5/x/ccv/types" + types3 "github.com/cosmos/interchain-security/v4/x/ccv/types" _ "google.golang.org/protobuf/types/known/durationpb" _ "google.golang.org/protobuf/types/known/timestamppb" io "io" diff --git a/x/ccv/provider/types/query.pb.go b/x/ccv/provider/types/query.pb.go index 099c2be2db..dbca4d229d 100644 --- a/x/ccv/provider/types/query.pb.go +++ b/x/ccv/provider/types/query.pb.go @@ -13,7 +13,7 @@ import ( grpc1 "github.com/cosmos/gogoproto/grpc" proto "github.com/cosmos/gogoproto/proto" github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" - types "github.com/cosmos/interchain-security/v5/x/ccv/types" + types "github.com/cosmos/interchain-security/v4/x/ccv/types" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" diff --git a/x/ccv/types/utils_test.go b/x/ccv/types/utils_test.go index 44f4ff6d27..f7ecd83197 100644 --- a/x/ccv/types/utils_test.go +++ b/x/ccv/types/utils_test.go @@ -10,7 +10,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/x/ccv/types" ) func TestAccumulateChanges(t *testing.T) { diff --git a/x/ccv/types/wire_test.go b/x/ccv/types/wire_test.go index 93512a5218..ab6692912e 100644 --- a/x/ccv/types/wire_test.go +++ b/x/ccv/types/wire_test.go @@ -12,8 +12,8 @@ import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/cosmos/interchain-security/v5/testutil/crypto" - "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/cosmos/interchain-security/v4/testutil/crypto" + "github.com/cosmos/interchain-security/v4/x/ccv/types" ) func TestPacketDataValidateBasic(t *testing.T) { From ad6ecbb1730182a63dd0c81bf79f7d2f06165072 Mon Sep 17 00:00:00 2001 From: MSalopek Date: Thu, 2 May 2024 10:07:33 +0200 Subject: [PATCH 015/102] chore: rm v5-provider; add v4.2.0; rm old versions (#1849) * chore: rm v5-provider; add v4.2.0; rm old versions * chore: rm v5-provider; add v4.2.0; rm old versions --- .github/dependabot.yml | 27 ++++----------------------- .mergify.yml | 40 ++++++++++++---------------------------- 2 files changed, 16 insertions(+), 51 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5356217e13..0a55a2d111 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -22,7 +22,7 @@ updates: directory: "/" schedule: interval: daily - target-branch: "release/v3.2.x" + target-branch: "release/v4.1.x" # Only allow automated security-related dependency updates on release branches. open-pull-requests-limit: 0 labels: @@ -32,7 +32,7 @@ updates: directory: "/" schedule: interval: daily - target-branch: "release/v3.3.x" + target-branch: "release/v4.1.x-lsm" # Only allow automated security-related dependency updates on release branches. open-pull-requests-limit: 0 labels: @@ -42,7 +42,7 @@ updates: directory: "/" schedule: interval: daily - target-branch: "release/v4.0.x" + target-branch: "release/v4.2.x" # Only allow automated security-related dependency updates on release branches. open-pull-requests-limit: 0 labels: @@ -52,17 +52,7 @@ updates: directory: "/" schedule: interval: daily - target-branch: "release/v4.1.x" - # Only allow automated security-related dependency updates on release branches. - open-pull-requests-limit: 0 - labels: - - dependencies - - - package-ecosystem: gomod - directory: "/" - schedule: - interval: daily - target-branch: "release/v4.1.x-lsm" + target-branch: "release/v4.2.x-lsm" # Only allow automated security-related dependency updates on release branches. open-pull-requests-limit: 0 labels: @@ -78,12 +68,3 @@ updates: labels: - dependencies - - package-ecosystem: gomod - directory: "/" - schedule: - interval: daily - target-branch: "release/v5.0.x-provider" - # Only allow automated security-related dependency updates on release branches. - open-pull-requests-limit: 0 - labels: - - dependencies diff --git a/.mergify.yml b/.mergify.yml index 11a69816ce..d373fe3fb9 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -10,46 +10,38 @@ queue_rules: - "#approved-reviews-by>1" pull_request_rules: - - name: Backport patches to the release/v3.2.x branch - conditions: - - base=main - - label=A:backport/v3.2.x - actions: - backport: - branches: - - release/v3.2.x - - name: Backport patches to the release/v3.3.x branch + - name: Backport patches to the release/v4.1.x branch conditions: - base=main - - label=A:backport/v3.3.x + - label=A:backport/v4.1.x actions: backport: branches: - - release/v3.3.x - - name: Backport patches to the release/v4.0.x branch + - release/v4.1.x + - name: Backport patches to the release/v4.1.x-lsm branch conditions: - base=main - - label=A:backport/v4.0.x + - label=A:backport/v4.1.x-lsm actions: backport: branches: - - release/v4.0.x - - name: Backport patches to the release/v4.1.x branch + - release/v4.1.x-lsm + - name: Backport patches to the release/v4.2.x branch conditions: - base=main - - label=A:backport/v4.1.x + - label=A:backport/v4.2.x actions: backport: branches: - - release/v4.1.x - - name: Backport patches to the release/v4.1.x-lsm branch + - release/v4.2.x + - name: Backport patches to the release/v4.2.x-lsm branch conditions: - base=main - - label=A:backport/v4.1.x-lsm + - label=A:backport/v4.2.x-lsm actions: backport: branches: - - release/v4.1.x-lsm + - release/v4.2.x-lsm - name: Backport patches to the release/v5.x branch conditions: - base=main @@ -58,11 +50,3 @@ pull_request_rules: backport: branches: - release/v5.x - - name: Backport patches to the release/v5.0.x-provider branch - conditions: - - base=main - - label=A:backport/v5.0.x-provider - actions: - backport: - branches: - - release/v5.0.x-provider From fab1224c2f21d583495d09c679aaf4b8a39ee68d Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Thu, 2 May 2024 13:55:02 +0200 Subject: [PATCH 016/102] chore: Add coderabbit configuration file (#1852) * Add coderabbit configuration file * Add release and feat base branches to coderabbit * Change config to not auto-post review status on unreviewed PRs --- .coderabbit.yml | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .coderabbit.yml diff --git a/.coderabbit.yml b/.coderabbit.yml new file mode 100644 index 0000000000..6083e965da --- /dev/null +++ b/.coderabbit.yml @@ -0,0 +1,40 @@ +language: "en" +early_access: false +reviews: + request_changes_workflow: false + high_level_summary: true + poem: false + review_status: false + collapse_walkthrough: true + path_filters: + - "!api/" + path_instructions: + - path: "**/*.go" + instructions: "Review the Golang code for conformity with the Uber Golang style guide, highlighting any deviations." + - path: "tests/e2e/*" + instructions: | + "Assess the e2e test code assessing sufficient code coverage for the changes associated in the pull request" + - path: "tests/integration/*" + instructions: | + "Assess the e2e test code assessing sufficient code coverage for the changes associated in the pull request" + - path: "**/*_test.go" + instructions: | + "Assess the unit test code assessing sufficient code coverage for the changes associated in the pull request" + - path: "**/*.md" + instructions: | + "Assess the documentation for misspellings, grammatical errors, missing documentation and correctness" + - path: ".changelog/*" + instructions: | + "Assess the changes in the changelog for correctness and completeness, particularly flagging missing changes" + auto_review: + enabled: true + ignore_title_keywords: + - "WIP" + - "DO NOT MERGE" + drafts: false + base_branches: + - "main" + - "feat/*" + - "release/*" +chat: + auto_reply: true From 28d6982e7a3e671bd54840840210c6dac6d884a1 Mon Sep 17 00:00:00 2001 From: Marius Poke Date: Thu, 2 May 2024 14:23:15 +0200 Subject: [PATCH 017/102] refactor: remove redundant code from MakeConsumerGenesis (#1807) * remove redundant code from MakeConsumerGenesis * fix tests --- testutil/keeper/expectations.go | 2 +- x/ccv/provider/keeper/proposal.go | 26 ++------------------------ x/ccv/provider/keeper/proposal_test.go | 2 +- 3 files changed, 4 insertions(+), 26 deletions(-) diff --git a/testutil/keeper/expectations.go b/testutil/keeper/expectations.go index 5a37f3a164..34446bd0c3 100644 --- a/testutil/keeper/expectations.go +++ b/testutil/keeper/expectations.go @@ -58,7 +58,7 @@ func GetMocksForMakeConsumerGenesis(ctx sdk.Context, mocks *MockedKeepers, mocks.MockClientKeeper.EXPECT().GetSelfConsensusState(gomock.Any(), clienttypes.GetSelfHeight(ctx)).Return(&ibctmtypes.ConsensusState{}, nil).Times(1), - mocks.MockStakingKeeper.EXPECT().IterateLastValidatorPowers(gomock.Any(), gomock.Any()).Times(1), + mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Times(1), } } diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 1d2bf19a4f..2e8c9b46d4 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -14,7 +14,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" tmtypes "github.com/cometbft/cometbft/types" @@ -260,29 +259,8 @@ func (k Keeper) MakeConsumerGenesis( return gen, nil, errorsmod.Wrapf(clienttypes.ErrConsensusStateNotFound, "error %s getting self consensus state for: %s", err, height) } - var lastPowers []stakingtypes.LastValidatorPower - - k.stakingKeeper.IterateLastValidatorPowers(ctx, func(addr sdk.ValAddress, power int64) (stop bool) { - lastPowers = append(lastPowers, stakingtypes.LastValidatorPower{Address: addr.String(), Power: power}) - return false - }) - - var bondedValidators []stakingtypes.Validator - - for _, p := range lastPowers { - addr, err := sdk.ValAddressFromBech32(p.Address) - if err != nil { - return gen, nil, err - } - - val, found := k.stakingKeeper.GetValidator(ctx, addr) - if !found { - return gen, nil, errorsmod.Wrapf(stakingtypes.ErrNoValidatorFound, "error getting validator from LastValidatorPowers: %s", err) - } - - // gather all the bonded validators in order to construct the consumer validator set for consumer chain `chainID` - bondedValidators = append(bondedValidators, val) - } + // get the bonded validators from the staking module + bondedValidators := k.stakingKeeper.GetLastValidators(ctx) if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { // in a Top-N chain, we automatically opt in all validators that belong to the top N diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index 9b769d2f94..80b4531b75 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -173,7 +173,7 @@ func TestCreateConsumerClient(t *testing.T) { mocks.MockStakingKeeper.EXPECT().UnbondingTime(gomock.Any()).Times(0) mocks.MockClientKeeper.EXPECT().CreateClient(gomock.Any(), gomock.Any(), gomock.Any()).Times(0) mocks.MockClientKeeper.EXPECT().GetSelfConsensusState(gomock.Any(), gomock.Any()).Times(0) - mocks.MockStakingKeeper.EXPECT().IterateLastValidatorPowers(gomock.Any(), gomock.Any()).Times(0) + mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Times(0) }, expClientCreated: false, }, From 5bb6b1cf945f7720c74809427418cee947f909d8 Mon Sep 17 00:00:00 2001 From: insumity Date: Thu, 2 May 2024 14:57:39 +0200 Subject: [PATCH 018/102] refactor: nit naming changes (#1854) * small naming fix * reverts name to ComputeNextValidators because the semantics of ComputeNextEpochConsumerValSet were different in v4.1 --- x/ccv/provider/keeper/validator_set_update_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/ccv/provider/keeper/validator_set_update_test.go b/x/ccv/provider/keeper/validator_set_update_test.go index 396a74fb1f..3ce6b34e90 100644 --- a/x/ccv/provider/keeper/validator_set_update_test.go +++ b/x/ccv/provider/keeper/validator_set_update_test.go @@ -327,7 +327,7 @@ func TestSetConsumerValSet(t *testing.T) { require.Equal(t, nextValidators, nextCurrentValidators) } -func TestComputeNextEpochConsumerValSetConsiderAll(t *testing.T) { +func TestFilterValidatorsConsiderAll(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() @@ -367,7 +367,7 @@ func TestComputeNextEpochConsumerValSetConsiderAll(t *testing.T) { require.Equal(t, expectedValidators, actualValidators) } -func TestComputeNextEpochConsumerValSetConsiderOnlyOptIn(t *testing.T) { +func TestFilterValidatorsConsiderOnlyOptIn(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() From 4f33a3e554b9b38d9b448aea2d9837c11b9412df Mon Sep 17 00:00:00 2001 From: bernd-m <43466467+bermuell@users.noreply.github.com> Date: Wed, 8 May 2024 16:43:53 +0200 Subject: [PATCH 019/102] docs: ADR for Security Aggregation solution (#1866) * ADR CosmoLayer: Initial draft * update adr: slinky api * cleanup * Apply suggestions from code review Co-authored-by: Marius Poke * addressed comments * cleanup of pseudo code, power sources * renaming to security aggregation * mv adr * removed comments * minor change in code example * Apply suggestions from code review Grammar Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: Marius Poke Co-authored-by: Jehan Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- docs/docs/adrs/adr-016-securityaggregation.md | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 docs/docs/adrs/adr-016-securityaggregation.md diff --git a/docs/docs/adrs/adr-016-securityaggregation.md b/docs/docs/adrs/adr-016-securityaggregation.md new file mode 100644 index 0000000000..9ff4134854 --- /dev/null +++ b/docs/docs/adrs/adr-016-securityaggregation.md @@ -0,0 +1,199 @@ +# ADR 016: Security aggregation + +## Changelog + +- 2024-04-24: Initial draft of ADR + +## Status + +Draft + +## Context + +Security Aggregation enables staking of tokens from external sources such as Ethereum or Bitcoin to Cosmos blockchains. By integrating Security Aggregation, a Cosmos blockchain can be secured by both native tokens and external tokens (e.g. ETH, BTC). + +Security Aggregation consists of the following parts: + +- A mechanism for delegating external tokens to Cosmos validators, such as Babylon or EigenLayer AVS contract. +- An oracle that tracks how much external stake has been delegated to each Cosmos validator and provides price feeds for external tokens. +- Power mixing: a mechanism to combine external and native stake to derive the power of each validator. +- A reward distribution protocol that enables sending back rewards to the external source. + +External staking information is received from an oracle together with price information of related stakes. +The CosmosLayer derives validator powers based on external and native staking information and initiates rewarding of external depositors. + +This ADR describes the _Cosmos modules_ of the solution. + +## Alternative Approaches + +### Rewards +As an alternative to sending rewards back to the external chains, stakers could be rewarded on the Cosmos chain. +This would require a mapping of external addresses to addresses on Cosmos chain for each staker on external source. +In addition detailed external staking information such as staking addresses, amount of stakes per staker and validator, etc. have to be provided by the oracle. + +## Decision + +### Rewards will be sent back to external chains instead of paying rewards for external stakers on Cosmos chain +Rewards will be sent back to external chains instead of paying rewards for external stakers on Cosmos chain +- due to amount of additional staking information to be sent and tracked by the oracle +- due to the additional complexity of managing external and Cosmos addresses + +## Detailed Design + +The `Power Mixing` feature and `Reward Distribution` protocol are an integral part of the Security Aggregation solution. +The `Power Mixing` module provides the capability of deriving validator power based on stake originated from external sources such as Ethereum/Bitcoin and the native staking module. +The `Reward Distribution` manages the process of sending rewards to external stakers. + +### Power Mixing + +Power Mixing provides the final validator powers based on staking information of the native chain and the external stakes. The information about external staking and related price feeds are received from an oracle. +Once the final validator powers are determined the result is submitted to the underlying CometBFT consensus layer by [updating](https://docs.cometbft.com/v0.38/spec/abci/abci++_app_requirements#updating-the-validator-set) the validator set. + +Requirements: + +- validator updates are performed on each EndBlock +- a validator's power is determined based on its native on-chain stakes and external stakes +- price information of staked tokens is used to determine a validator’s power, e.g. price ratio (price of native on-chain token / price of external stake) +- price information of native/external tokens are received from an oracle +- staking information from external sources received from the oracle +- native staking information are received from the `Cosmos SDK Staking Module` +- set of validator stakes from oracle always have the current price, full set of validators, and current stakes + +The `Power Mixing` implementation +- queries current validators and their powers from [x/staking](https://github.com/cosmos/cosmos-sdk/blob/a6f3fbfbeb7ea94bda6369a7163a523e118a123c/x/staking/types/staking.pb.go#L415) +and from oracle (see below). +- calculates power updates by mixing power values of external and internal sources +Following pseudocode snippet shows a possible implementation of how power mixing +feature works. +```golang +// PowerSource is an abstract entity providing validator powers which +// are used by the mixer. This can be an oracle, staking module or an +// IBC connected bridge. +type PowerSource interface { + GetValidatorUpdates() []abci.ValidatorUpdate +} + +// MixPowers calculates power updates by mixing validator powers from different sources +func (k *Keeper) MixPowers(source ...PowerSource) []abci.ValidatorUpdate { + var valUpdate []abci.ValidatorUpdate + for _, ps := range source { + // mix powers from two sets of validator updates an return set of validator updates + // with aggregated powers + valUpdate = mixPower(valUpdate, ps.GetValidatorUpdates()) + } + return valUpdate +} + +func (k *keeper) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + // GetPowerSources (including local staking module) + registeredPowerSource := GetPowerSources() + return k.MixPowers(registeredPowerSource...) +} +``` + +#### Integration with `ICS provider` +The provider module updates the validator set on CometBFT instead of the SDK staking module (x/staking). The provider implementation will intervene in this behavior and ensure that the validator updates are taken from the `Power Mixing` feature. + +External power sources are managed by the provider module. Only registered power sources can provide input to the `Power Mixing` feature. +Power sources will be assigned a unique identifier which will be used by the oracle, provider module and the power mixing and rewarding feature. + +Updates with the next validator set are sent to consumer chains on each epoch (see `EndBlockVSU()`). +When collecting the validator updates for each consumer chain (see [`QueueVSCPackets()`](https://pkg.go.dev/github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper#Keeper.QueueVSCPackets)), the validator powers of the bonded validators will be updated with the validator powers from the external sources using the `Power Mixing` module. +These updates are sent as part of the VSC packets to all registered consumer chains. + +#### Integration with `ICS consumer` +Consumer chains receive validator updates as part of VSC packets from the provider. +These packets contain validator powers which were already mixed with external staked powers. + + +### Queries + +```protobuf +// GetValidatorUpdates returns the power mixed validator results from the provided sources +service Query { + rpc GetValidatorUpdates(PowerMixedValUpdateRequest) PowerMixedValUpdateResponse {}; +} + +// PowerMixedValUpdateRequest contains the list of power sources on which the +// power mixing should be based on +message PowerMixedValUpdateRequest { + repeated PowerSource sources; +} + +// PowerMixedValUpdateResponse returns the validator set with the updated powers +// from the power mixing feature +message PowerMixedValUpdateResponse { + repeated abci.ValidatorUpdate val_set +} +``` + + +The following queries will be provided by the oracle + +```protobuf +service Query { + rpc GetExtValidators(GetExtValidatorRequest) returns (ExtValidatorsResponse) { + option (google.api.http).get = "oracle/v1/get_validators"; + }; +} + +message GetExtValidatorRequest {} + +// ExtValidatorsResponse is the response from GetExtValidators queries +message ExtValidatorsResponse { + repeated ExtValPower powers; +} + +// ExtValPower represents a validator with its staking and token information, +// where: +// `power_source_identifier` is the identifier of the registered power source +// `validator_address` is the address of the validator +// `stakes` is the total amount of stakes for a validator +// `denom` is the source token of the stake e.g. ETH,BTC +// `price_ratio` is the ratio of price of the external token to the price of the 'local' token +message ExtValPower { + string power_source_identifier; + string validator_address; + uint64 stakes; + string denom; + float price_ratio; +} + +// GetPrice returns a price feed for a given token +service Query { + rpc GetPrice(GetPriceRequest) returns (GetPriceResponse) { + option (google.api.http).get = "/oracle/v1/get_price"; + }; +} +``` + +For security reasons the amount of external stakes needs to be limited. Limitation of external staking could be driven by governance and is not subject of this version of the ADR. + +### Reward Handler + +For native staked tokens the `Distribution Module` of the Cosmos SDK is taking care of sending the rewards to stakers. +For stakes originated from external chains (Ethereum/Bitcoin) the `Reward Handler` module sends rewards to EigenLayer/Babylon. +The transfer of rewards is done using a bridge between the Cosmos chain and the external provider chain. + +Note: currently there's no support paying rewards on EigenLayer (see [here](https://www.coindesk.com/tech/2024/04/10/eigenlayer-cryptos-biggest-project-launch-this-year-is-still-missing-crucial-functionality/)) + +## Consequences + +### Positive + +* Allow external depositors to stake their tokens to secure a Cosmos chain + +### Negative +* Dependency to external sources e.g (price feeds) for validator power calculation +* Security impact + +### Neutral +* Additional complexity for staking + +## Questions: +- Slashing: subject of this ADR? (Defined but [not activated](https://www.coindesk.com/tech/2024/04/10/eigenlayer-cryptos-biggest-project-launch-this-year-is-still-missing-crucial-functionality/) currently on EigenLayer). + +## References + +- [EigenLayer](https://docs.eigenlayer.xyz/) +- [Babylon](https://babylonchain.io/) From 3328e9bf9721c404902ec7c62a4fbd7c1bf8c2f0 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Wed, 8 May 2024 18:29:45 +0200 Subject: [PATCH 020/102] chore: Make coderabbit ignore docs folder (#1864) Make bot ignore docs folder --- .coderabbit.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.coderabbit.yml b/.coderabbit.yml index 6083e965da..b805c87ba2 100644 --- a/.coderabbit.yml +++ b/.coderabbit.yml @@ -8,6 +8,7 @@ reviews: collapse_walkthrough: true path_filters: - "!api/" + - "!docs/" path_instructions: - path: "**/*.go" instructions: "Review the Golang code for conformity with the Uber Golang style guide, highlighting any deviations." From 8329a27e6aa72a4678576e83b2450795141f20f0 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Fri, 10 May 2024 09:53:32 +0200 Subject: [PATCH 021/102] chore: Fix codespell config (#1869) * Add optin and opt-in to codespell ignored words * Fix typo on multiple * Fix typo on assignment --- .github/.codespellignore | 2 ++ tests/e2e/steps_partial_set_security.go | 2 +- tests/integration/distribution.go | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/.codespellignore b/.github/.codespellignore index 3fc9037efb..0c7e0a95c1 100644 --- a/.github/.codespellignore +++ b/.github/.codespellignore @@ -1,3 +1,5 @@ clienta connectiona sover +optin +opt-in diff --git a/tests/e2e/steps_partial_set_security.go b/tests/e2e/steps_partial_set_security.go index 4c63e479df..eb551d80ed 100644 --- a/tests/e2e/steps_partial_set_security.go +++ b/tests/e2e/steps_partial_set_security.go @@ -55,7 +55,7 @@ func stepsOptInChain() []Step { }, // Οpt in "alice" and "bob" so the chain is not empty when it is about to start. Note, that "alice" and "bob" use // the provider's public key (see `UseConsumerKey` is set to `false` in `getDefaultValidators`) and hence do not - // need a consumer-key assigment. + // need a consumer-key assignment. { Action: OptInAction{ Chain: ChainID("consu"), diff --git a/tests/integration/distribution.go b/tests/integration/distribution.go index 5d62b2ee35..a71edf7f77 100644 --- a/tests/integration/distribution.go +++ b/tests/integration/distribution.go @@ -1117,7 +1117,7 @@ func (s *CCVTestSuite) TestMultiConsumerRewardsDistribution() { bundle.GetCtx(), accountKeeper.GetModuleAccount(bundle.GetCtx(), consumertypes.ConsumerToSendToProviderName).GetAddress(), ) - s.Require().Len(pool, 1, "consumer reward pool cannot have mutiple token denoms") + s.Require().Len(pool, 1, "consumer reward pool cannot have multiple token denoms") rewardsPerConsumer = pool[0] } From 78dad0c478998190217c682d040bba9af343bdbf Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Mon, 13 May 2024 16:15:08 +0200 Subject: [PATCH 022/102] feat: PSS - Add minimum power in top N & power shaping params to consumer chain list (#1863) * Add minimum power in top N to the list-consumer-chains query * Add test for MinPowerInTop_N * Add changelog entry * Update x/ccv/provider/keeper/keeper_test.go Co-authored-by: insumity * Add other validator shaping params to consumer chain list * Add power shaping params to query test * Adjust changelog for extra fields * Add changelog entry for API breaking --------- Co-authored-by: insumity --- ...um-power-in-topN-to-consumer-chain-list.md | 2 + ...um-power-in-topN-to-consumer-chain-list.md | 2 + .../ccv/provider/v1/query.proto | 13 + scripts/protocgen.sh | 0 x/ccv/provider/keeper/keeper.go | 74 ++- x/ccv/provider/keeper/keeper_test.go | 77 ++- x/ccv/provider/types/query.pb.go | 462 +++++++++++++----- 7 files changed, 507 insertions(+), 123 deletions(-) create mode 100644 .changelog/unreleased/api-breaking/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md create mode 100644 .changelog/unreleased/features/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md mode change 100644 => 100755 scripts/protocgen.sh diff --git a/.changelog/unreleased/api-breaking/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md b/.changelog/unreleased/api-breaking/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md new file mode 100644 index 0000000000..482702f26b --- /dev/null +++ b/.changelog/unreleased/api-breaking/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md @@ -0,0 +1,2 @@ +- Changes the `list-consumer-chains` query to include a `min_power_in_top_N` field, as well as fields for all power shaping parameters of the consumer. + ([\#1863](https://github.com/cosmos/interchain-security/pull/1863)) \ No newline at end of file diff --git a/.changelog/unreleased/features/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md b/.changelog/unreleased/features/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md new file mode 100644 index 0000000000..482702f26b --- /dev/null +++ b/.changelog/unreleased/features/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md @@ -0,0 +1,2 @@ +- Changes the `list-consumer-chains` query to include a `min_power_in_top_N` field, as well as fields for all power shaping parameters of the consumer. + ([\#1863](https://github.com/cosmos/interchain-security/pull/1863)) \ No newline at end of file diff --git a/proto/interchain_security/ccv/provider/v1/query.proto b/proto/interchain_security/ccv/provider/v1/query.proto index 139a7386de..d34bfd3e4c 100644 --- a/proto/interchain_security/ccv/provider/v1/query.proto +++ b/proto/interchain_security/ccv/provider/v1/query.proto @@ -164,6 +164,19 @@ message Chain { string client_id = 2; // If chain with `chainID` is a Top-N chain, i.e., enforces at least one validator to validate chain `chainID` uint32 top_N = 3; + // If the chain is a Top-N chain, this is the minimum power required to be in the top N. + // Otherwise, this is -1. + int64 min_power_in_top_N = 4; + // Corresponds to the maximum power (percentage-wise) a validator can have on the consumer chain. + uint32 validators_power_cap = 5; + // Corresponds to the maximum number of validators that can validate a consumer chain. + // Only applicable to Opt In chains. Setting `validator_set_cap` on a Top N chain is a no-op. + uint32 validator_set_cap = 6; + // Corresponds to a list of provider consensus addresses of validators that are the ONLY ones that can validate + // the consumer chain. + repeated string allowlist = 7; + // Corresponds to a list of provider consensus addresses of validators that CANNOT validate the consumer chain. + repeated string denylist = 8; } message QueryValidatorConsumerAddrRequest { diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh old mode 100644 new mode 100755 diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index bc3664bfd7..98930a3085 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -253,12 +253,44 @@ func (k Keeper) GetAllConsumerChains(ctx sdk.Context) (chains []types.Chain) { chainID := string(iterator.Key()[1:]) clientID := string(iterator.Value()) - topN, _ := k.GetTopN(ctx, chainID) + topN, found := k.GetTopN(ctx, chainID) + + var minPowerInTopN int64 + if found && topN > 0 { + res, err := k.ComputeMinPowerToOptIn(ctx, chainID, k.stakingKeeper.GetLastValidators(ctx), topN) + if err != nil { + k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err) + minPowerInTopN = -1 + } else { + minPowerInTopN = res + } + } else { + minPowerInTopN = -1 + } + + validatorSetCap, _ := k.GetValidatorSetCap(ctx, chainID) + validatorsPowerCap, _ := k.GetValidatorsPowerCap(ctx, chainID) + allowlist := k.GetAllowList(ctx, chainID) + strAllowlist := make([]string, len(allowlist)) + for i, addr := range allowlist { + strAllowlist[i] = addr.String() + } + + denylist := k.GetDenyList(ctx, chainID) + strDenylist := make([]string, len(denylist)) + for i, addr := range denylist { + strDenylist[i] = addr.String() + } chains = append(chains, types.Chain{ - ChainId: chainID, - ClientId: clientID, - Top_N: topN, + ChainId: chainID, + ClientId: clientID, + Top_N: topN, + MinPowerInTop_N: minPowerInTopN, + ValidatorSetCap: validatorSetCap, + ValidatorsPowerCap: validatorsPowerCap, + Allowlist: strAllowlist, + Denylist: strDenylist, }) } @@ -1446,6 +1478,23 @@ func (k Keeper) SetAllowlist( store.Set(types.AllowlistCapKey(chainID, providerAddr), []byte{}) } +// GetAllowList returns all allowlisted validators +func (k Keeper) GetAllowList( + ctx sdk.Context, + chainID string, +) (providerConsAddresses []types.ProviderConsAddress) { + store := ctx.KVStore(k.storeKey) + key := types.ChainIdWithLenKey(types.AllowlistPrefix, chainID) + iterator := sdk.KVStorePrefixIterator(store, key) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + providerConsAddresses = append(providerConsAddresses, types.NewProviderConsAddress(iterator.Key()[len(key):])) + } + + return providerConsAddresses +} + // IsAllowlisted returns `true` if validator with `providerAddr` has been allowlisted on chain `chainID` func (k Keeper) IsAllowlisted( ctx sdk.Context, @@ -1496,6 +1545,23 @@ func (k Keeper) SetDenylist( store.Set(types.DenylistCapKey(chainID, providerAddr), []byte{}) } +// GetDenyList returns all denylisted validators +func (k Keeper) GetDenyList( + ctx sdk.Context, + chainID string, +) (providerConsAddresses []types.ProviderConsAddress) { + store := ctx.KVStore(k.storeKey) + key := types.ChainIdWithLenKey(types.DenylistPrefix, chainID) + iterator := sdk.KVStorePrefixIterator(store, key) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + providerConsAddresses = append(providerConsAddresses, types.NewProviderConsAddress(iterator.Key()[len(key):])) + } + + return providerConsAddresses +} + // IsDenylisted returns `true` if validator with `providerAddr` has been denylisted on chain `chainID` func (k Keeper) IsDenylisted( ctx sdk.Context, diff --git a/x/ccv/provider/keeper/keeper_test.go b/x/ccv/provider/keeper/keeper_test.go index d172a96dba..894011cfab 100644 --- a/x/ccv/provider/keeper/keeper_test.go +++ b/x/ccv/provider/keeper/keeper_test.go @@ -8,6 +8,7 @@ import ( "time" ibctesting "github.com/cosmos/ibc-go/v7/testing" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" @@ -20,6 +21,8 @@ import ( testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" + + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) const consumer = "consumer" @@ -395,17 +398,85 @@ func TestVscSendTimestamp(t *testing.T) { // TestGetAllConsumerChains tests GetAllConsumerChains behaviour correctness func TestGetAllConsumerChains(t *testing.T) { - pk, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + pk, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() chainIDs := []string{"chain-2", "chain-1", "chain-4", "chain-3"} + + // mock the validator set + vals := []stakingtypes.Validator{ + {OperatorAddress: "cosmosvaloper1c4k24jzduc365kywrsvf5ujz4ya6mwympnc4en"}, // 50 power + {OperatorAddress: "cosmosvaloper196ax4vc0lwpxndu9dyhvca7jhxp70rmcvrj90c"}, // 150 power + {OperatorAddress: "cosmosvaloper1clpqr4nrk4khgkxj78fcwwh6dl3uw4epsluffn"}, // 300 power + {OperatorAddress: "cosmosvaloper1tflk30mq5vgqjdly92kkhhq3raev2hnz6eete3"}, // 500 power + } + powers := []int64{50, 150, 300, 500} // sum = 1000 + mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Return(vals).AnyTimes() + + for i, val := range vals { + mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), val.GetOperator()).Return(powers[i]).AnyTimes() + } + + // set Top N parameters, client ids and expected result + topNs := []uint32{0, 70, 90, 100} + expectedMinPowerInTopNs := []int64{ + -1, // Top N is 0, so not a Top N chain + 300, // 500 and 300 are in Top 70% + 150, // 150 is also in the top 90%, + 50, // everyone is in the top 100% + } + + validatorSetCaps := []uint32{0, 5, 10, 20} + validatorPowerCaps := []uint32{0, 5, 10, 33} + allowlists := [][]types.ProviderConsAddress{ + {}, + {types.NewProviderConsAddress([]byte("providerAddr1")), types.NewProviderConsAddress([]byte("providerAddr2"))}, + {types.NewProviderConsAddress([]byte("providerAddr3"))}, + {}, + } + + denylists := [][]types.ProviderConsAddress{ + {types.NewProviderConsAddress([]byte("providerAddr4")), types.NewProviderConsAddress([]byte("providerAddr5"))}, + {}, + {types.NewProviderConsAddress([]byte("providerAddr6"))}, + {}, + } + expectedGetAllOrder := []types.Chain{} for i, chainID := range chainIDs { clientID := fmt.Sprintf("client-%d", len(chainIDs)-i) - topN := uint32(i) + topN := topNs[i] pk.SetConsumerClientId(ctx, chainID, clientID) pk.SetTopN(ctx, chainID, topN) - expectedGetAllOrder = append(expectedGetAllOrder, types.Chain{ChainId: chainID, ClientId: clientID, Top_N: topN}) + pk.SetValidatorSetCap(ctx, chainID, validatorSetCaps[i]) + pk.SetValidatorsPowerCap(ctx, chainID, validatorPowerCaps[i]) + for _, addr := range allowlists[i] { + pk.SetAllowlist(ctx, chainID, addr) + } + for _, addr := range denylists[i] { + pk.SetDenylist(ctx, chainID, addr) + } + strAllowlist := make([]string, len(allowlists[i])) + for j, addr := range allowlists[i] { + strAllowlist[j] = addr.String() + } + + strDenylist := make([]string, len(denylists[i])) + for j, addr := range denylists[i] { + strDenylist[j] = addr.String() + } + + expectedGetAllOrder = append(expectedGetAllOrder, + types.Chain{ + ChainId: chainID, + ClientId: clientID, + Top_N: topN, + MinPowerInTop_N: expectedMinPowerInTopNs[i], + ValidatorSetCap: validatorSetCaps[i], + ValidatorsPowerCap: validatorPowerCaps[i], + Allowlist: strAllowlist, + Denylist: strDenylist, + }) } // sorting by chainID sort.Slice(expectedGetAllOrder, func(i, j int) bool { diff --git a/x/ccv/provider/types/query.pb.go b/x/ccv/provider/types/query.pb.go index dbca4d229d..b2a57c1c2f 100644 --- a/x/ccv/provider/types/query.pb.go +++ b/x/ccv/provider/types/query.pb.go @@ -378,6 +378,19 @@ type Chain struct { ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` // If chain with `chainID` is a Top-N chain, i.e., enforces at least one validator to validate chain `chainID` Top_N uint32 `protobuf:"varint,3,opt,name=top_N,json=topN,proto3" json:"top_N,omitempty"` + // If the chain is a Top-N chain, this is the minimum power required to be in the top N. + // Otherwise, this is -1. + MinPowerInTop_N int64 `protobuf:"varint,4,opt,name=min_power_in_top_N,json=minPowerInTopN,proto3" json:"min_power_in_top_N,omitempty"` + // Corresponds to the maximum power (percentage-wise) a validator can have on the consumer chain. + ValidatorsPowerCap uint32 `protobuf:"varint,5,opt,name=validators_power_cap,json=validatorsPowerCap,proto3" json:"validators_power_cap,omitempty"` + // Corresponds to the maximum number of validators that can validate a consumer chain. + // Only applicable to Opt In chains. Setting `validator_set_cap` on a Top N chain is a no-op. + ValidatorSetCap uint32 `protobuf:"varint,6,opt,name=validator_set_cap,json=validatorSetCap,proto3" json:"validator_set_cap,omitempty"` + // Corresponds to a list of provider consensus addresses of validators that are the ONLY ones that can validate + // the consumer chain. + Allowlist []string `protobuf:"bytes,7,rep,name=allowlist,proto3" json:"allowlist,omitempty"` + // Corresponds to a list of provider consensus addresses of validators that CANNOT validate the consumer chain. + Denylist []string `protobuf:"bytes,8,rep,name=denylist,proto3" json:"denylist,omitempty"` } func (m *Chain) Reset() { *m = Chain{} } @@ -434,6 +447,41 @@ func (m *Chain) GetTop_N() uint32 { return 0 } +func (m *Chain) GetMinPowerInTop_N() int64 { + if m != nil { + return m.MinPowerInTop_N + } + return 0 +} + +func (m *Chain) GetValidatorsPowerCap() uint32 { + if m != nil { + return m.ValidatorsPowerCap + } + return 0 +} + +func (m *Chain) GetValidatorSetCap() uint32 { + if m != nil { + return m.ValidatorSetCap + } + return 0 +} + +func (m *Chain) GetAllowlist() []string { + if m != nil { + return m.Allowlist + } + return nil +} + +func (m *Chain) GetDenylist() []string { + if m != nil { + return m.Denylist + } + return nil +} + type QueryValidatorConsumerAddrRequest struct { // The id of the consumer chain ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` @@ -1586,122 +1634,129 @@ func init() { } var fileDescriptor_422512d7b7586cd7 = []byte{ - // 1834 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcd, 0x6f, 0xdc, 0x5a, - 0x15, 0x8f, 0x93, 0x34, 0x24, 0x37, 0xaf, 0x7d, 0xe5, 0xb6, 0xbc, 0x97, 0x3a, 0xe9, 0x4c, 0x9e, - 0x1f, 0x3c, 0xd2, 0x2f, 0x3b, 0x99, 0xf2, 0x44, 0xbf, 0xd2, 0x34, 0x93, 0x49, 0xd2, 0x51, 0xda, - 0x66, 0xea, 0xa6, 0x01, 0x15, 0x84, 0xeb, 0xd8, 0xb7, 0x13, 0xab, 0x1e, 0x5f, 0xc7, 0xd7, 0x33, - 0xed, 0xa8, 0xaa, 0x44, 0x59, 0x40, 0x57, 0xa8, 0xe2, 0x63, 0xdf, 0x0d, 0x4b, 0x36, 0x08, 0xf1, - 0x2f, 0xd0, 0x1d, 0x85, 0x6e, 0x10, 0x8b, 0x80, 0x52, 0x16, 0x88, 0x15, 0xaa, 0x90, 0x58, 0x21, - 0x21, 0x5f, 0x5f, 0x7f, 0xcd, 0x38, 0x33, 0x9e, 0x49, 0x58, 0x25, 0x73, 0x7d, 0xef, 0xef, 0x9c, - 0xdf, 0xf1, 0x39, 0xe7, 0x9e, 0x9f, 0x81, 0x64, 0x58, 0x2e, 0x72, 0xb4, 0x6d, 0xd5, 0xb0, 0x14, - 0x82, 0xb4, 0xba, 0x63, 0xb8, 0x4d, 0x49, 0xd3, 0x1a, 0x92, 0xed, 0xe0, 0x86, 0xa1, 0x23, 0x47, - 0x6a, 0xcc, 0x49, 0x3b, 0x75, 0xe4, 0x34, 0x45, 0xdb, 0xc1, 0x2e, 0x86, 0x9f, 0xa7, 0x1c, 0x10, - 0x35, 0xad, 0x21, 0x06, 0x07, 0xc4, 0xc6, 0x1c, 0x3f, 0x55, 0xc5, 0xb8, 0x6a, 0x22, 0x49, 0xb5, - 0x0d, 0x49, 0xb5, 0x2c, 0xec, 0xaa, 0xae, 0x81, 0x2d, 0xe2, 0x43, 0xf0, 0x27, 0xab, 0xb8, 0x8a, - 0xe9, 0xbf, 0x92, 0xf7, 0x1f, 0x5b, 0xcd, 0xb3, 0x33, 0xf4, 0xd7, 0x56, 0xfd, 0x91, 0xe4, 0x1a, - 0x35, 0x44, 0x5c, 0xb5, 0x66, 0xb3, 0x0d, 0x85, 0x2c, 0xae, 0x86, 0x5e, 0xf8, 0x67, 0x66, 0xf7, - 0x3b, 0xd3, 0x98, 0x93, 0xc8, 0xb6, 0xea, 0x20, 0x5d, 0xd1, 0xb0, 0x45, 0xea, 0xb5, 0xf0, 0xc4, - 0x37, 0x3a, 0x9c, 0x78, 0x62, 0x38, 0x88, 0x6d, 0x9b, 0x72, 0x91, 0xa5, 0x23, 0xa7, 0x66, 0x58, - 0xae, 0xa4, 0x39, 0x4d, 0xdb, 0xc5, 0xd2, 0x63, 0xd4, 0x0c, 0x18, 0x9e, 0xd2, 0x30, 0xa9, 0x61, - 0xa2, 0xf8, 0x24, 0xfd, 0x1f, 0xfe, 0x23, 0xe1, 0x12, 0x98, 0xbc, 0xeb, 0x85, 0x73, 0x89, 0x99, - 0x5d, 0x45, 0x16, 0x22, 0x06, 0x91, 0xd1, 0x4e, 0x1d, 0x11, 0x17, 0x9e, 0x02, 0xa3, 0xbe, 0x6d, - 0x43, 0x9f, 0xe0, 0xa6, 0xb9, 0x99, 0x31, 0xf9, 0x2b, 0xf4, 0x77, 0x59, 0x17, 0x9e, 0x81, 0xa9, - 0xf4, 0x93, 0xc4, 0xc6, 0x16, 0x41, 0xf0, 0x7b, 0xe0, 0x68, 0xd5, 0x5f, 0x52, 0x88, 0xab, 0xba, - 0x88, 0x9e, 0x1f, 0x2f, 0xcc, 0x8a, 0xfb, 0xbd, 0xb1, 0xc6, 0x9c, 0xd8, 0x82, 0x75, 0xcf, 0x3b, - 0x57, 0x1c, 0x7e, 0xb3, 0x9b, 0x1f, 0x90, 0x3f, 0xaa, 0xc6, 0xd6, 0x84, 0x29, 0xc0, 0x27, 0x8c, - 0x2f, 0x79, 0x70, 0x81, 0xd7, 0x82, 0xda, 0x42, 0x2a, 0x78, 0xca, 0x3c, 0x2b, 0x82, 0x11, 0x6a, - 0x9e, 0x4c, 0x70, 0xd3, 0x43, 0x33, 0xe3, 0x85, 0xb3, 0x62, 0x86, 0x24, 0x12, 0x29, 0x88, 0xcc, - 0x4e, 0x0a, 0x67, 0xc0, 0x37, 0xdb, 0x4d, 0xdc, 0x73, 0x55, 0xc7, 0xad, 0x38, 0xd8, 0xc6, 0x44, - 0x35, 0x43, 0x6f, 0x5e, 0x72, 0x60, 0xa6, 0xfb, 0x5e, 0xe6, 0xdb, 0xf7, 0xc1, 0x98, 0x1d, 0x2c, - 0xb2, 0x88, 0x5d, 0xcf, 0xe6, 0x1e, 0x03, 0x5f, 0xd4, 0x75, 0xc3, 0xcb, 0xee, 0x08, 0x3a, 0x02, - 0x14, 0x66, 0xc0, 0x17, 0x69, 0x9e, 0x60, 0xbb, 0xcd, 0xe9, 0x1f, 0x73, 0xe9, 0x04, 0x13, 0x5b, - 0xc3, 0x37, 0xdd, 0xe6, 0xf3, 0x7c, 0x4f, 0x3e, 0xcb, 0xa8, 0x86, 0x1b, 0xaa, 0x99, 0xea, 0xf2, - 0x06, 0x38, 0x42, 0x4d, 0x77, 0x48, 0x45, 0x38, 0x09, 0xc6, 0x34, 0xd3, 0x40, 0x96, 0xeb, 0x3d, - 0x1b, 0xa4, 0xcf, 0x46, 0xfd, 0x85, 0xb2, 0x0e, 0x4f, 0x80, 0x23, 0x2e, 0xb6, 0x95, 0x3b, 0x13, - 0x43, 0xd3, 0xdc, 0xcc, 0x51, 0x79, 0xd8, 0xc5, 0xf6, 0x1d, 0xe1, 0x27, 0x1c, 0xf8, 0x8c, 0xd2, - 0xdb, 0x54, 0x4d, 0x43, 0x57, 0x5d, 0xec, 0xc4, 0xe2, 0xe7, 0x74, 0xcf, 0x7e, 0x38, 0x0f, 0x8e, - 0x07, 0x4c, 0x14, 0x55, 0xd7, 0x1d, 0x44, 0x88, 0x6f, 0xb9, 0x08, 0x3f, 0xec, 0xe6, 0x8f, 0x35, - 0xd5, 0x9a, 0x79, 0x45, 0x60, 0x0f, 0x04, 0xf9, 0xe3, 0x60, 0xef, 0xa2, 0xbf, 0x72, 0x65, 0xf4, - 0xe5, 0xeb, 0xfc, 0xc0, 0x3f, 0x5e, 0xe7, 0x07, 0x84, 0x75, 0x20, 0x74, 0x72, 0x84, 0x85, 0xf8, - 0x0c, 0x38, 0x1e, 0x34, 0x86, 0xd0, 0x9c, 0xef, 0xd1, 0xc7, 0x5a, 0x6c, 0xbf, 0x67, 0xac, 0x9d, - 0x5a, 0x25, 0x66, 0x3c, 0x1b, 0xb5, 0x36, 0x5b, 0x1d, 0xa8, 0xb5, 0xd8, 0xef, 0x44, 0x2d, 0xe9, - 0x48, 0x44, 0xad, 0x2d, 0x92, 0x8c, 0x5a, 0x4b, 0xd4, 0x84, 0x49, 0x70, 0x8a, 0x02, 0x6e, 0x6c, - 0x3b, 0xd8, 0x75, 0x4d, 0x44, 0x7b, 0x41, 0x90, 0xb1, 0x7f, 0xe4, 0x58, 0x4f, 0x68, 0x79, 0xca, - 0xcc, 0xe4, 0xc1, 0x38, 0x31, 0x55, 0xb2, 0xad, 0xd4, 0x90, 0x8b, 0x1c, 0x6a, 0x61, 0x48, 0x06, - 0x74, 0xe9, 0xb6, 0xb7, 0x02, 0x0b, 0xe0, 0x6b, 0xb1, 0x0d, 0x8a, 0x6a, 0x9a, 0xf8, 0x89, 0x6a, - 0x69, 0x88, 0x72, 0x1f, 0x92, 0x4f, 0x44, 0x5b, 0x17, 0x83, 0x47, 0xf0, 0x07, 0x60, 0xc2, 0x42, - 0x4f, 0x5d, 0xc5, 0x41, 0xb6, 0x89, 0x2c, 0x83, 0x6c, 0x2b, 0x9a, 0x6a, 0xe9, 0x1e, 0x59, 0x44, - 0xd3, 0x6d, 0xbc, 0xc0, 0x8b, 0xfe, 0x3d, 0x22, 0x06, 0xf7, 0x88, 0xb8, 0x11, 0xdc, 0x23, 0xc5, - 0x51, 0xaf, 0xb1, 0xbd, 0xfa, 0x6b, 0x9e, 0x93, 0x3f, 0xf1, 0x50, 0xe4, 0x00, 0x64, 0x29, 0xc0, - 0x10, 0xce, 0x83, 0xb3, 0x94, 0x92, 0x8c, 0xaa, 0x06, 0x71, 0x91, 0x83, 0xf4, 0xa8, 0x64, 0x9e, - 0xa8, 0x8e, 0x5e, 0x42, 0x16, 0xae, 0x85, 0x35, 0xbb, 0x0c, 0xce, 0x65, 0xda, 0xcd, 0x22, 0xf2, - 0x09, 0x18, 0xd1, 0xe9, 0x0a, 0x6d, 0x83, 0x63, 0x32, 0xfb, 0x25, 0xe4, 0x58, 0x63, 0xf7, 0xcb, - 0x11, 0xe9, 0xb4, 0xfc, 0xca, 0xa5, 0xd0, 0xcc, 0x0b, 0x0e, 0x9c, 0xde, 0x67, 0x03, 0x43, 0x7e, - 0x08, 0x8e, 0xd9, 0xf1, 0x67, 0x41, 0xa3, 0x2d, 0x64, 0xea, 0x0a, 0x09, 0x58, 0xd6, 0xfd, 0x5b, - 0xf0, 0x84, 0x32, 0x38, 0x9a, 0xd8, 0x06, 0x27, 0x00, 0xcb, 0xdf, 0x52, 0x32, 0x9d, 0x4b, 0x30, - 0x07, 0x40, 0xd0, 0x4d, 0xca, 0x25, 0xfa, 0x32, 0x87, 0xe5, 0xd8, 0x8a, 0x70, 0x0b, 0x48, 0x94, - 0xcd, 0xa2, 0x69, 0x56, 0x54, 0xc3, 0x21, 0x9b, 0xaa, 0xb9, 0x84, 0x2d, 0x2f, 0xe5, 0x8a, 0xc9, - 0xe6, 0x57, 0x2e, 0x65, 0xb8, 0x15, 0x7f, 0xc5, 0x81, 0xd9, 0xec, 0x70, 0x2c, 0x5e, 0x3b, 0xe0, - 0xab, 0xb6, 0x6a, 0x38, 0x4a, 0x43, 0x35, 0xbd, 0xfb, 0x9f, 0x96, 0x01, 0x0b, 0xd9, 0x4a, 0xb6, - 0x90, 0xa9, 0x86, 0x13, 0x19, 0x0a, 0xcb, 0xcc, 0x8a, 0x12, 0xe0, 0x98, 0x9d, 0xd8, 0x22, 0xfc, - 0x9b, 0x03, 0x9f, 0x75, 0x3d, 0x05, 0x57, 0xf6, 0xab, 0xcd, 0xe2, 0xe4, 0x87, 0xdd, 0xfc, 0xa7, - 0x7e, 0x2b, 0x68, 0xdd, 0xd1, 0xde, 0xee, 0x3c, 0x9c, 0x7d, 0x5a, 0x4a, 0x0c, 0xa7, 0x75, 0x47, - 0x7b, 0x6f, 0x81, 0x0b, 0xe0, 0xa3, 0x70, 0xd7, 0x63, 0xd4, 0x64, 0x35, 0x36, 0x25, 0x46, 0xd3, - 0x8f, 0xe8, 0x4f, 0x3f, 0x62, 0xa5, 0xbe, 0x65, 0x1a, 0xda, 0x1a, 0x6a, 0xca, 0xe3, 0xc1, 0x89, - 0x35, 0xd4, 0x14, 0x4e, 0x02, 0xe8, 0xa7, 0xae, 0xea, 0xa8, 0x51, 0xe1, 0x3c, 0x04, 0x27, 0x12, - 0xab, 0xec, 0xb5, 0x94, 0xc1, 0x88, 0x4d, 0x57, 0xd8, 0xa5, 0x76, 0x2e, 0xe3, 0xbb, 0xf0, 0x8e, - 0xb0, 0xbc, 0x65, 0x00, 0xc2, 0x2a, 0x2b, 0xe4, 0x44, 0x06, 0xac, 0xdb, 0x2e, 0xd2, 0xcb, 0x56, - 0xd8, 0x1e, 0xb3, 0x4c, 0x5d, 0x3b, 0xac, 0xc6, 0xbb, 0x01, 0x85, 0xa3, 0xce, 0xe9, 0x46, 0xb8, - 0xaa, 0xb4, 0xbe, 0x29, 0x14, 0x94, 0xfe, 0x64, 0xb4, 0xa9, 0x92, 0x7c, 0x75, 0x88, 0x08, 0x3b, - 0x2c, 0xa3, 0x93, 0xd3, 0x54, 0x68, 0xec, 0xa6, 0x4a, 0x36, 0x30, 0xfb, 0x15, 0x34, 0xe3, 0xd4, - 0xeb, 0x91, 0xcb, 0x7c, 0x3d, 0x0a, 0x2a, 0x98, 0xeb, 0xc1, 0x24, 0xe3, 0x7a, 0x1e, 0xc0, 0x30, - 0x39, 0x82, 0xf0, 0x05, 0x04, 0xc3, 0xf4, 0xf3, 0x4b, 0x4f, 0xa7, 0xd7, 0xe4, 0xb9, 0xf4, 0x8b, - 0x77, 0x09, 0xd7, 0x6a, 0x06, 0x21, 0x06, 0xb6, 0xe4, 0x18, 0xa3, 0xff, 0xdb, 0x2c, 0x20, 0xfc, - 0x90, 0x03, 0xe7, 0xb3, 0x79, 0xc2, 0x88, 0x56, 0xc0, 0xb0, 0x13, 0x0c, 0xd4, 0x63, 0xc5, 0x6b, - 0x5e, 0xa2, 0xfd, 0x65, 0x37, 0xff, 0x45, 0xd5, 0x70, 0xb7, 0xeb, 0x5b, 0xa2, 0x86, 0x6b, 0x6c, - 0xc4, 0x67, 0x7f, 0x2e, 0x10, 0xfd, 0xb1, 0xe4, 0x36, 0x6d, 0x44, 0xc4, 0x12, 0xd2, 0xfe, 0xf4, - 0xdb, 0x0b, 0x80, 0x29, 0x80, 0x12, 0xd2, 0x64, 0x8a, 0x24, 0xcc, 0x83, 0x69, 0xea, 0xc1, 0xba, - 0xa9, 0x23, 0xe2, 0xde, 0xb7, 0x34, 0x6c, 0x3d, 0x32, 0x9c, 0x1a, 0xd2, 0x37, 0x89, 0x96, 0x21, - 0x29, 0x7f, 0x1a, 0x8c, 0x1c, 0xe9, 0xe7, 0x99, 0xdb, 0x06, 0x80, 0x0d, 0xa2, 0x29, 0x04, 0x59, - 0xba, 0x12, 0x8a, 0x29, 0x56, 0x5a, 0x5f, 0x66, 0x2a, 0xad, 0x4d, 0xa2, 0xdd, 0x43, 0x96, 0x1e, - 0xdd, 0xa0, 0x7e, 0x91, 0x1d, 0x6f, 0xb4, 0xac, 0x17, 0x7e, 0x7f, 0x1a, 0x1c, 0xa1, 0x0e, 0xc1, - 0x3d, 0x0e, 0x9c, 0x4c, 0x93, 0x29, 0xf0, 0x46, 0x26, 0x8b, 0x1d, 0xb4, 0x11, 0xbf, 0x78, 0x00, - 0x04, 0x3f, 0x24, 0xc2, 0xf2, 0x8f, 0xde, 0xfd, 0xfd, 0xe7, 0x83, 0x0b, 0x70, 0xbe, 0xbb, 0xee, - 0x0d, 0x53, 0x9b, 0xe9, 0x20, 0xe9, 0x59, 0xf0, 0x36, 0x9e, 0xc3, 0x77, 0x1c, 0x6b, 0x60, 0xc9, - 0x7a, 0x81, 0x0b, 0xbd, 0x7b, 0x98, 0x10, 0x52, 0xfc, 0x8d, 0xfe, 0x01, 0x18, 0xc3, 0xcb, 0x94, - 0xe1, 0x45, 0x38, 0xd7, 0x03, 0x43, 0x5f, 0x62, 0xc1, 0x17, 0x83, 0x60, 0x62, 0x1f, 0xdd, 0x44, - 0xe0, 0xad, 0x3e, 0x3d, 0x4b, 0x95, 0x68, 0xfc, 0xed, 0x43, 0x42, 0x63, 0xa4, 0x6f, 0x52, 0xd2, - 0x45, 0x78, 0xa3, 0x57, 0xd2, 0x9e, 0x52, 0x76, 0x5c, 0x25, 0x54, 0x3f, 0xf0, 0xbf, 0x1c, 0xf8, - 0x34, 0x5d, 0x86, 0x11, 0xb8, 0xd6, 0xb7, 0xd3, 0xed, 0x7a, 0x8f, 0xbf, 0x75, 0x38, 0x60, 0x2c, - 0x00, 0xab, 0x34, 0x00, 0x8b, 0x70, 0xa1, 0x8f, 0x00, 0x60, 0x3b, 0xc6, 0xff, 0x5f, 0xc1, 0x50, - 0x9f, 0x2a, 0x8f, 0xe0, 0x4a, 0x76, 0xaf, 0x3b, 0x09, 0x3d, 0x7e, 0xf5, 0xc0, 0x38, 0x8c, 0xf8, - 0x22, 0x25, 0x7e, 0x15, 0x5e, 0xce, 0xf0, 0x21, 0x2b, 0x00, 0x52, 0x12, 0x83, 0x4f, 0x0a, 0xe5, - 0xf8, 0x95, 0xdc, 0x17, 0xe5, 0x14, 0x01, 0xd8, 0x17, 0xe5, 0x34, 0xfd, 0xd6, 0x1f, 0xe5, 0xc4, - 0x7d, 0x09, 0xff, 0xc0, 0xb1, 0xb1, 0x2c, 0x21, 0xdd, 0xe0, 0xf5, 0xec, 0x2e, 0xa6, 0x29, 0x42, - 0x7e, 0xa1, 0xef, 0xf3, 0x8c, 0xda, 0x25, 0x4a, 0xad, 0x00, 0x67, 0xbb, 0x53, 0x73, 0x19, 0x80, - 0xff, 0xad, 0x0b, 0xfe, 0x72, 0x10, 0x7c, 0x9e, 0x41, 0x8b, 0xc1, 0xf5, 0xec, 0x2e, 0x66, 0xd2, - 0x80, 0x7c, 0xe5, 0xf0, 0x00, 0x59, 0x10, 0xd6, 0x68, 0x10, 0x96, 0xe1, 0x52, 0xf7, 0x20, 0x38, - 0x21, 0x62, 0x94, 0xd3, 0x0e, 0xc5, 0x54, 0x7c, 0x6d, 0x09, 0xff, 0xd9, 0xa6, 0x1d, 0x93, 0x92, - 0x88, 0xc0, 0x1e, 0x6e, 0xd5, 0x7d, 0x04, 0x2a, 0x5f, 0x3c, 0x08, 0x04, 0x63, 0x5d, 0xa4, 0xac, - 0xaf, 0xc1, 0x2b, 0xdd, 0x59, 0x07, 0xd2, 0x54, 0x69, 0xbd, 0xc0, 0x7e, 0x31, 0xc8, 0x3e, 0xfc, - 0x65, 0xd0, 0x82, 0x70, 0x23, 0xbb, 0xd3, 0xd9, 0x95, 0x2a, 0x7f, 0xff, 0x90, 0x51, 0x59, 0x74, - 0xae, 0xd2, 0xe8, 0x7c, 0x09, 0x2f, 0xf6, 0xdc, 0xdf, 0x0d, 0x1d, 0xfe, 0x86, 0x03, 0xe3, 0x31, - 0xb9, 0x05, 0xbf, 0xdd, 0xc3, 0xeb, 0x8a, 0xcb, 0x36, 0xfe, 0x52, 0xef, 0x07, 0x99, 0xff, 0xb3, - 0xd4, 0xff, 0xb3, 0x70, 0x26, 0xc3, 0xdb, 0xf5, 0x9d, 0xfc, 0x59, 0x50, 0xd0, 0x9d, 0x85, 0x57, - 0x2f, 0x05, 0x9d, 0x49, 0x0b, 0xf6, 0x52, 0xd0, 0xd9, 0x34, 0x61, 0x2f, 0xd3, 0x09, 0xf6, 0x40, - 0x14, 0xc3, 0x52, 0x22, 0x7d, 0x18, 0x9f, 0x3b, 0x7f, 0x37, 0x08, 0xce, 0x64, 0xd6, 0x69, 0xf0, - 0x7e, 0xbf, 0xc3, 0x64, 0x47, 0xa9, 0xc9, 0x6f, 0x1e, 0x36, 0x2c, 0x0b, 0xd3, 0x03, 0x1a, 0xa6, - 0x0d, 0x28, 0xf7, 0x3c, 0xb9, 0x2a, 0x36, 0x72, 0xa2, 0x88, 0x49, 0xcf, 0x5a, 0xc5, 0xe1, 0x73, - 0xf8, 0xeb, 0x41, 0xf0, 0xf5, 0x2c, 0x92, 0x0f, 0x56, 0x0e, 0x30, 0x98, 0xa4, 0xea, 0x58, 0xfe, - 0xee, 0x21, 0x22, 0xb2, 0x48, 0x3d, 0xa4, 0x91, 0x7a, 0x00, 0xbf, 0xdb, 0x4b, 0xa4, 0x42, 0x28, - 0xc5, 0x53, 0xa0, 0xb1, 0xac, 0x4a, 0x8b, 0xd7, 0x7f, 0x38, 0xf6, 0xe5, 0x37, 0x4d, 0x60, 0xc2, - 0xe5, 0xec, 0x94, 0x3a, 0x08, 0x5c, 0x7e, 0xe5, 0xa0, 0x30, 0xbd, 0x5f, 0x98, 0x98, 0xe2, 0x28, - 0xf5, 0x08, 0x48, 0x69, 0x10, 0x2d, 0x16, 0x8c, 0xe2, 0x77, 0xde, 0xec, 0xe5, 0xb8, 0xb7, 0x7b, - 0x39, 0xee, 0x6f, 0x7b, 0x39, 0xee, 0xd5, 0xfb, 0xdc, 0xc0, 0xdb, 0xf7, 0xb9, 0x81, 0x3f, 0xbf, - 0xcf, 0x0d, 0x3c, 0x98, 0x6f, 0xd7, 0xfb, 0x91, 0xbd, 0x0b, 0xa1, 0xbd, 0xc6, 0xb7, 0xa4, 0xa7, - 0x2d, 0xa3, 0x4a, 0xd3, 0x46, 0x64, 0x6b, 0x84, 0x7e, 0x90, 0xbe, 0xf8, 0xbf, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x21, 0x1e, 0x15, 0x5a, 0x72, 0x1d, 0x00, 0x00, + // 1937 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcb, 0x6f, 0x1b, 0xc7, + 0x19, 0xd7, 0x52, 0xb2, 0x22, 0x8d, 0xe2, 0x47, 0xc6, 0x6e, 0x42, 0xaf, 0x64, 0x52, 0xd9, 0xb4, + 0x29, 0x2d, 0xdb, 0xbb, 0x12, 0xdd, 0xa0, 0x8e, 0x13, 0x45, 0x16, 0x45, 0xdb, 0x21, 0xec, 0xc4, + 0xcc, 0x5a, 0x56, 0x0b, 0xb7, 0xe8, 0x7a, 0xbd, 0x3b, 0xa1, 0x16, 0x5e, 0xee, 0xac, 0x76, 0x86, + 0x74, 0x08, 0x23, 0x40, 0xd3, 0x43, 0x9b, 0x53, 0x11, 0xf4, 0x01, 0xf4, 0x98, 0x4b, 0x8f, 0xbd, + 0x14, 0x45, 0xff, 0x85, 0xe6, 0xd6, 0xb4, 0xb9, 0x14, 0x3d, 0xb8, 0x85, 0xdc, 0x43, 0xd1, 0x53, + 0x11, 0x14, 0xe8, 0xa9, 0x40, 0xb1, 0xb3, 0xb3, 0x2f, 0x72, 0x45, 0x2e, 0x29, 0xe5, 0x24, 0xed, + 0xcc, 0x37, 0xbf, 0xef, 0x31, 0xdf, 0x63, 0x7e, 0x04, 0x8a, 0xe5, 0x50, 0xe4, 0x19, 0xbb, 0xba, + 0xe5, 0x68, 0x04, 0x19, 0x1d, 0xcf, 0xa2, 0x3d, 0xc5, 0x30, 0xba, 0x8a, 0xeb, 0xe1, 0xae, 0x65, + 0x22, 0x4f, 0xe9, 0xae, 0x29, 0x7b, 0x1d, 0xe4, 0xf5, 0x64, 0xd7, 0xc3, 0x14, 0xc3, 0x57, 0x32, + 0x0e, 0xc8, 0x86, 0xd1, 0x95, 0xc3, 0x03, 0x72, 0x77, 0x4d, 0x5c, 0x6a, 0x61, 0xdc, 0xb2, 0x91, + 0xa2, 0xbb, 0x96, 0xa2, 0x3b, 0x0e, 0xa6, 0x3a, 0xb5, 0xb0, 0x43, 0x02, 0x08, 0xf1, 0x4c, 0x0b, + 0xb7, 0x30, 0xfb, 0x57, 0xf1, 0xff, 0xe3, 0xab, 0x65, 0x7e, 0x86, 0x7d, 0x3d, 0xec, 0xbc, 0xaf, + 0x50, 0xab, 0x8d, 0x08, 0xd5, 0xdb, 0x2e, 0x17, 0xa8, 0xe6, 0x31, 0x35, 0xb2, 0x22, 0x38, 0xb3, + 0x7a, 0xd0, 0x99, 0xee, 0x9a, 0x42, 0x76, 0x75, 0x0f, 0x99, 0x9a, 0x81, 0x1d, 0xd2, 0x69, 0x47, + 0x27, 0xbe, 0x31, 0xe4, 0xc4, 0x63, 0xcb, 0x43, 0x5c, 0x6c, 0x89, 0x22, 0xc7, 0x44, 0x5e, 0xdb, + 0x72, 0xa8, 0x62, 0x78, 0x3d, 0x97, 0x62, 0xe5, 0x11, 0xea, 0x85, 0x1e, 0x9e, 0x35, 0x30, 0x69, + 0x63, 0xa2, 0x05, 0x4e, 0x06, 0x1f, 0xc1, 0x96, 0x74, 0x05, 0x2c, 0xbe, 0xe7, 0x87, 0x73, 0x8b, + 0xab, 0xbd, 0x89, 0x1c, 0x44, 0x2c, 0xa2, 0xa2, 0xbd, 0x0e, 0x22, 0x14, 0x9e, 0x05, 0x73, 0x81, + 0x6e, 0xcb, 0x2c, 0x0a, 0xcb, 0x42, 0x65, 0x5e, 0x7d, 0x8e, 0x7d, 0x37, 0x4c, 0xe9, 0x09, 0x58, + 0xca, 0x3e, 0x49, 0x5c, 0xec, 0x10, 0x04, 0xbf, 0x07, 0x8e, 0xb7, 0x82, 0x25, 0x8d, 0x50, 0x9d, + 0x22, 0x76, 0x7e, 0xa1, 0xba, 0x2a, 0x1f, 0x74, 0x63, 0xdd, 0x35, 0xb9, 0x0f, 0xeb, 0xae, 0x7f, + 0xae, 0x36, 0xf3, 0xd9, 0xd3, 0xf2, 0x94, 0xfa, 0x7c, 0x2b, 0xb1, 0x26, 0x2d, 0x01, 0x31, 0xa5, + 0x7c, 0xcb, 0x87, 0x0b, 0xad, 0x96, 0xf4, 0x3e, 0xa7, 0xc2, 0x5d, 0x6e, 0x59, 0x0d, 0xcc, 0x32, + 0xf5, 0xa4, 0x28, 0x2c, 0x4f, 0x57, 0x16, 0xaa, 0x2b, 0x72, 0x8e, 0x24, 0x92, 0x19, 0x88, 0xca, + 0x4f, 0x4a, 0xe7, 0xc1, 0x37, 0x07, 0x55, 0xdc, 0xa5, 0xba, 0x47, 0x9b, 0x1e, 0x76, 0x31, 0xd1, + 0xed, 0xc8, 0x9a, 0x8f, 0x05, 0x50, 0x19, 0x2d, 0xcb, 0x6d, 0xfb, 0x3e, 0x98, 0x77, 0xc3, 0x45, + 0x1e, 0xb1, 0xb7, 0xf2, 0x99, 0xc7, 0xc1, 0x37, 0x4d, 0xd3, 0xf2, 0xb3, 0x3b, 0x86, 0x8e, 0x01, + 0xa5, 0x0a, 0x78, 0x35, 0xcb, 0x12, 0xec, 0x0e, 0x18, 0xfd, 0x63, 0x21, 0xdb, 0xc1, 0x94, 0x68, + 0x74, 0xd3, 0x03, 0x36, 0xaf, 0x8f, 0x65, 0xb3, 0x8a, 0xda, 0xb8, 0xab, 0xdb, 0x99, 0x26, 0xff, + 0xaa, 0x00, 0x8e, 0x31, 0xdd, 0x43, 0x72, 0x11, 0x2e, 0x82, 0x79, 0xc3, 0xb6, 0x90, 0x43, 0xfd, + 0xbd, 0x02, 0xdb, 0x9b, 0x0b, 0x16, 0x1a, 0x26, 0x3c, 0x0d, 0x8e, 0x51, 0xec, 0x6a, 0xef, 0x16, + 0xa7, 0x97, 0x85, 0xca, 0x71, 0x75, 0x86, 0x62, 0xf7, 0x5d, 0xb8, 0x02, 0x60, 0xdb, 0x72, 0x34, + 0x17, 0x3f, 0x46, 0x9e, 0x66, 0x39, 0x5a, 0x20, 0x31, 0xb3, 0x2c, 0x54, 0xa6, 0xd5, 0x13, 0x6d, + 0xcb, 0x69, 0xfa, 0x1b, 0x0d, 0x67, 0xdb, 0x97, 0x5d, 0x05, 0x67, 0xba, 0xba, 0x6d, 0x99, 0x3a, + 0xc5, 0x1e, 0xe1, 0x47, 0x0c, 0xdd, 0x2d, 0x1e, 0x63, 0x78, 0x30, 0xde, 0x63, 0x87, 0xb6, 0x74, + 0x17, 0xae, 0x80, 0x17, 0xa2, 0x55, 0x8d, 0x20, 0xca, 0xc4, 0x67, 0x99, 0xf8, 0xc9, 0x68, 0xe3, + 0x2e, 0xa2, 0xbe, 0xec, 0x12, 0x98, 0xd7, 0x6d, 0x1b, 0x3f, 0xb6, 0x2d, 0x42, 0x8b, 0xcf, 0x2d, + 0x4f, 0x57, 0xe6, 0xd5, 0x78, 0x01, 0x8a, 0x60, 0xce, 0x44, 0x4e, 0x8f, 0x6d, 0xce, 0xb1, 0xcd, + 0xe8, 0x5b, 0xfa, 0x89, 0x00, 0x5e, 0x66, 0x77, 0xb4, 0x13, 0x42, 0x26, 0x92, 0xc0, 0x1b, 0x5d, + 0xc2, 0x70, 0x1d, 0x9c, 0x0a, 0xaf, 0x43, 0xd3, 0x4d, 0xd3, 0x43, 0x84, 0x04, 0xd1, 0xab, 0xc1, + 0x2f, 0x9f, 0x96, 0x4f, 0xf4, 0xf4, 0xb6, 0x7d, 0x55, 0xe2, 0x1b, 0x92, 0x7a, 0x32, 0x94, 0xdd, + 0x0c, 0x56, 0xae, 0xce, 0x7d, 0xfc, 0x69, 0x79, 0xea, 0x9f, 0x9f, 0x96, 0xa7, 0xa4, 0x3b, 0x40, + 0x1a, 0x66, 0x08, 0xcf, 0x93, 0xf3, 0xe0, 0x54, 0xd8, 0xdd, 0x22, 0x75, 0x81, 0x45, 0x27, 0x8d, + 0x84, 0xbc, 0xaf, 0x6c, 0xd0, 0xb5, 0x66, 0x42, 0x79, 0x3e, 0xd7, 0x06, 0x74, 0x0d, 0x71, 0xad, + 0x4f, 0xff, 0x30, 0xd7, 0xd2, 0x86, 0xc4, 0xae, 0x0d, 0x44, 0x92, 0xbb, 0xd6, 0x17, 0x35, 0x69, + 0x11, 0x9c, 0x65, 0x80, 0xdb, 0xbb, 0x1e, 0xa6, 0xd4, 0x46, 0xac, 0xa1, 0x85, 0x65, 0xf7, 0x27, + 0x81, 0x37, 0xb6, 0xbe, 0x5d, 0xae, 0xa6, 0x0c, 0x16, 0x88, 0xad, 0x93, 0x5d, 0xad, 0x8d, 0x28, + 0xf2, 0x98, 0x86, 0x69, 0x15, 0xb0, 0xa5, 0x77, 0xfc, 0x15, 0x58, 0x05, 0x5f, 0x4b, 0x08, 0x68, + 0x2c, 0x8f, 0x74, 0xc7, 0x40, 0xcc, 0xf7, 0x69, 0xf5, 0x74, 0x2c, 0xba, 0x19, 0x6e, 0xc1, 0x1f, + 0x80, 0xa2, 0x83, 0x3e, 0xa0, 0x9a, 0x87, 0x5c, 0x1b, 0x39, 0x16, 0xd9, 0xd5, 0x0c, 0xdd, 0x31, + 0x7d, 0x67, 0x11, 0x2b, 0x99, 0x85, 0xaa, 0x28, 0x07, 0xc3, 0x50, 0x0e, 0x87, 0xa1, 0xbc, 0x1d, + 0x0e, 0xc3, 0xda, 0x9c, 0xdf, 0x9d, 0x3f, 0xf9, 0x5b, 0x59, 0x50, 0x5f, 0xf4, 0x51, 0xd4, 0x10, + 0x64, 0x2b, 0xc4, 0x90, 0x2e, 0x82, 0x15, 0xe6, 0x92, 0x8a, 0x5a, 0x16, 0xa1, 0xc8, 0x43, 0x66, + 0x5c, 0xf7, 0x8f, 0x75, 0xcf, 0xac, 0x23, 0x07, 0xb7, 0xa3, 0xc6, 0x73, 0x1d, 0x5c, 0xc8, 0x25, + 0xcd, 0x23, 0xf2, 0x22, 0x98, 0x35, 0xd9, 0x0a, 0xeb, 0xe5, 0xf3, 0x2a, 0xff, 0x92, 0x4a, 0x7c, + 0x3a, 0x05, 0x3d, 0x05, 0x99, 0xac, 0x85, 0x34, 0xea, 0x91, 0x9a, 0x8f, 0x04, 0x70, 0xee, 0x00, + 0x01, 0x8e, 0xfc, 0x00, 0x9c, 0x70, 0x93, 0x7b, 0xe1, 0xb4, 0xa8, 0xe6, 0x6a, 0x6d, 0x29, 0x58, + 0x3e, 0xc2, 0xfa, 0xf0, 0xa4, 0x06, 0x38, 0x9e, 0x12, 0x83, 0x45, 0xc0, 0xf3, 0xb7, 0x9e, 0x4e, + 0xe7, 0x3a, 0x2c, 0x01, 0x10, 0xb6, 0xc4, 0x46, 0x9d, 0x5d, 0xe6, 0x8c, 0x9a, 0x58, 0x91, 0x6e, + 0x03, 0x85, 0x79, 0xb3, 0x69, 0xdb, 0x4d, 0xdd, 0xf2, 0xc8, 0x8e, 0x6e, 0x6f, 0x61, 0xc7, 0x4f, + 0xb9, 0x5a, 0xba, 0x83, 0x37, 0xea, 0x39, 0x46, 0xfb, 0xaf, 0x05, 0xb0, 0x9a, 0x1f, 0x8e, 0xc7, + 0x6b, 0x0f, 0xbc, 0xe0, 0xea, 0x96, 0xa7, 0x75, 0x75, 0xdb, 0x7f, 0xc4, 0xb0, 0x32, 0xe0, 0x21, + 0xbb, 0x91, 0x2f, 0x64, 0xba, 0xe5, 0xc5, 0x8a, 0xa2, 0x32, 0x73, 0xe2, 0x04, 0x38, 0xe1, 0xa6, + 0x44, 0xa4, 0xff, 0x08, 0xe0, 0xe5, 0x91, 0xa7, 0xe0, 0x8d, 0x83, 0x6a, 0xb3, 0xb6, 0xf8, 0xe5, + 0xd3, 0xf2, 0x4b, 0x41, 0x2b, 0xe8, 0x97, 0x18, 0x6c, 0x77, 0x3e, 0xce, 0x01, 0x2d, 0x25, 0x81, + 0xd3, 0x2f, 0x31, 0xd8, 0x5b, 0xe0, 0x06, 0x78, 0x3e, 0x92, 0x7a, 0x84, 0x7a, 0xbc, 0xc6, 0x96, + 0xe4, 0xf8, 0x09, 0x27, 0x07, 0x4f, 0x38, 0xb9, 0xd9, 0x79, 0x68, 0x5b, 0xc6, 0x2d, 0xd4, 0x53, + 0x17, 0xc2, 0x13, 0xb7, 0x50, 0x4f, 0x3a, 0x03, 0x60, 0x90, 0xba, 0xba, 0xa7, 0xc7, 0x85, 0xf3, + 0x00, 0x9c, 0x4e, 0xad, 0xf2, 0x6b, 0x69, 0x80, 0x59, 0x97, 0xad, 0xf0, 0xc9, 0x7c, 0x21, 0xe7, + 0x5d, 0xf8, 0x47, 0x78, 0xde, 0x72, 0x00, 0xe9, 0x26, 0x2f, 0xe4, 0x54, 0x06, 0xdc, 0x71, 0x29, + 0x32, 0x1b, 0x4e, 0xd4, 0x1e, 0xf3, 0x3c, 0x1d, 0xf7, 0x78, 0x8d, 0x8f, 0x02, 0x8a, 0xde, 0x6b, + 0xe7, 0x92, 0xf3, 0xb7, 0xef, 0xa6, 0x50, 0x58, 0xfa, 0x8b, 0x89, 0x41, 0x9c, 0xbe, 0x3a, 0x44, + 0xa4, 0x3d, 0x9e, 0xd1, 0xe9, 0x27, 0x61, 0xa4, 0xec, 0x6d, 0x9d, 0x6c, 0x63, 0xfe, 0x15, 0x36, + 0xe3, 0xcc, 0xf1, 0x28, 0xe4, 0x1e, 0x8f, 0x92, 0x0e, 0xd6, 0xc6, 0x50, 0xc9, 0x7d, 0xbd, 0x08, + 0x60, 0x94, 0x1c, 0x61, 0xf8, 0x42, 0x07, 0xa3, 0xf4, 0x0b, 0x4a, 0xcf, 0x64, 0x63, 0xf2, 0x42, + 0xf6, 0xe0, 0xdd, 0xc2, 0xed, 0xb6, 0x45, 0x88, 0x85, 0x1d, 0x35, 0xe1, 0xd1, 0x57, 0xf6, 0x16, + 0x90, 0x7e, 0x28, 0x80, 0x8b, 0xf9, 0x2c, 0xe1, 0x8e, 0x36, 0xc1, 0x8c, 0x17, 0xb2, 0x82, 0xf9, + 0xda, 0x9b, 0x7e, 0xa2, 0xfd, 0xf5, 0x69, 0xf9, 0xd5, 0x96, 0x45, 0x77, 0x3b, 0x0f, 0x65, 0x03, + 0xb7, 0x39, 0x4f, 0xe1, 0x7f, 0x2e, 0x11, 0xf3, 0x91, 0x42, 0x7b, 0x2e, 0x22, 0x72, 0x1d, 0x19, + 0x7f, 0xfe, 0xdd, 0x25, 0xc0, 0x69, 0x4c, 0x1d, 0x19, 0x2a, 0x43, 0x92, 0xd6, 0xc1, 0x32, 0xb3, + 0xe0, 0x8e, 0x6d, 0x22, 0x42, 0xef, 0x39, 0x06, 0x76, 0xde, 0xb7, 0xbc, 0x36, 0x32, 0x77, 0x88, + 0x91, 0x23, 0x29, 0x7f, 0x1a, 0x3e, 0x39, 0xb2, 0xcf, 0x73, 0xb3, 0x2d, 0x00, 0xbb, 0xc4, 0xd0, + 0x08, 0x72, 0x4c, 0x2d, 0x62, 0x84, 0xbc, 0xb4, 0x5e, 0xcb, 0x55, 0x5a, 0x3b, 0xc4, 0xb8, 0x8b, + 0x1c, 0x33, 0x9e, 0xa0, 0x41, 0x91, 0x9d, 0xea, 0xf6, 0xad, 0x57, 0xff, 0x70, 0x0e, 0x1c, 0x63, + 0x06, 0xc1, 0x7d, 0x01, 0x9c, 0xc9, 0xe2, 0x5a, 0xf0, 0x5a, 0x2e, 0x8d, 0x43, 0x08, 0x9e, 0xb8, + 0x79, 0x08, 0x84, 0x20, 0x24, 0xd2, 0xf5, 0x1f, 0x7d, 0xf1, 0x8f, 0x9f, 0x17, 0x36, 0xe0, 0xfa, + 0x68, 0xf2, 0x1e, 0xa5, 0x36, 0x27, 0x73, 0xca, 0x93, 0xf0, 0x36, 0x3e, 0x84, 0x5f, 0x08, 0xbc, + 0x81, 0xa5, 0xeb, 0x05, 0x6e, 0x8c, 0x6f, 0x61, 0x8a, 0x0d, 0x8a, 0xd7, 0x26, 0x07, 0xe0, 0x1e, + 0xbe, 0xce, 0x3c, 0xbc, 0x0c, 0xd7, 0xc6, 0xf0, 0x30, 0xe0, 0x89, 0xf0, 0xa3, 0x02, 0x28, 0x1e, + 0x40, 0xfe, 0x08, 0xbc, 0x3d, 0xa1, 0x65, 0x99, 0x3c, 0x53, 0x7c, 0xe7, 0x88, 0xd0, 0xb8, 0xd3, + 0x6f, 0x33, 0xa7, 0x6b, 0xf0, 0xda, 0xb8, 0x4e, 0xfb, 0x74, 0xdf, 0xa3, 0x5a, 0x44, 0xe1, 0xe0, + 0xff, 0x04, 0xf0, 0x52, 0x36, 0x97, 0x24, 0xf0, 0xd6, 0xc4, 0x46, 0x0f, 0x92, 0x56, 0xf1, 0xf6, + 0xd1, 0x80, 0xf1, 0x00, 0xdc, 0x64, 0x01, 0xd8, 0x84, 0x1b, 0x13, 0x04, 0x00, 0xbb, 0x09, 0xff, + 0xff, 0x1d, 0x3e, 0xea, 0x33, 0xe9, 0x11, 0xbc, 0x91, 0xdf, 0xea, 0x61, 0x44, 0x4f, 0xbc, 0x79, + 0x68, 0x1c, 0xee, 0xf8, 0x26, 0x73, 0xfc, 0x0d, 0xf8, 0x7a, 0x8e, 0x5f, 0xe3, 0x22, 0x96, 0x9b, + 0x7a, 0xf8, 0x64, 0xb8, 0x9c, 0x1c, 0xc9, 0x13, 0xb9, 0x9c, 0x41, 0x00, 0x27, 0x72, 0x39, 0x8b, + 0xbf, 0x4d, 0xe6, 0x72, 0x6a, 0x5e, 0xc2, 0x3f, 0x0a, 0xfc, 0x59, 0x96, 0xa2, 0x6e, 0xf0, 0xad, + 0xfc, 0x26, 0x66, 0x31, 0x42, 0x71, 0x63, 0xe2, 0xf3, 0xdc, 0xb5, 0x2b, 0xcc, 0xb5, 0x2a, 0x5c, + 0x1d, 0xed, 0x1a, 0xe5, 0x00, 0xc1, 0x0f, 0x76, 0xf0, 0x97, 0x05, 0xf0, 0x4a, 0x0e, 0x2e, 0x06, + 0xef, 0xe4, 0x37, 0x31, 0x17, 0x07, 0x14, 0x9b, 0x47, 0x07, 0xc8, 0x83, 0x70, 0x8b, 0x05, 0xe1, + 0x3a, 0xdc, 0x1a, 0x1d, 0x04, 0x2f, 0x42, 0x8c, 0x73, 0xda, 0x63, 0x98, 0x5a, 0xc0, 0x2d, 0xe1, + 0xbf, 0x06, 0xb8, 0x63, 0x9a, 0x12, 0x11, 0x38, 0xc6, 0x54, 0x3d, 0x80, 0xa0, 0x8a, 0xb5, 0xc3, + 0x40, 0x70, 0xaf, 0x6b, 0xcc, 0xeb, 0x37, 0xe1, 0xd5, 0xd1, 0x5e, 0x87, 0xd4, 0x54, 0xeb, 0x1f, + 0x60, 0xbf, 0x28, 0xf0, 0x5f, 0x2f, 0x73, 0x70, 0x41, 0xb8, 0x9d, 0xdf, 0xe8, 0xfc, 0x4c, 0x55, + 0xbc, 0x77, 0xc4, 0xa8, 0x3c, 0x3a, 0x6f, 0xb0, 0xe8, 0xbc, 0x06, 0x2f, 0x8f, 0xdd, 0xdf, 0x2d, + 0x13, 0xfe, 0x56, 0x00, 0x0b, 0x09, 0xba, 0x05, 0xbf, 0x3d, 0xc6, 0x75, 0x25, 0x69, 0x9b, 0x78, + 0x65, 0xfc, 0x83, 0xdc, 0xfe, 0x55, 0x66, 0xff, 0x0a, 0xac, 0xe4, 0xb8, 0xdd, 0xc0, 0xc8, 0x9f, + 0x85, 0x05, 0x3d, 0x9c, 0x78, 0x8d, 0x53, 0xd0, 0xb9, 0xb8, 0xe0, 0x38, 0x05, 0x9d, 0x8f, 0x13, + 0x8e, 0xf3, 0x3a, 0xc1, 0x3e, 0x88, 0x66, 0x39, 0x5a, 0xcc, 0x0f, 0x93, 0xef, 0xce, 0xdf, 0x17, + 0xc0, 0xf9, 0xdc, 0x3c, 0x0d, 0xde, 0x9b, 0xf4, 0x31, 0x39, 0x94, 0x6a, 0x8a, 0x3b, 0x47, 0x0d, + 0xcb, 0xc3, 0x74, 0x9f, 0x85, 0x69, 0x1b, 0xaa, 0x63, 0xbf, 0x5c, 0x35, 0x17, 0x79, 0x71, 0xc4, + 0x94, 0x27, 0xfd, 0xe4, 0xf0, 0x43, 0xf8, 0x9b, 0x02, 0xf8, 0x7a, 0x1e, 0xca, 0x07, 0x9b, 0x87, + 0x78, 0x98, 0x64, 0xf2, 0x58, 0xf1, 0xbd, 0x23, 0x44, 0xe4, 0x91, 0x7a, 0xc0, 0x22, 0x75, 0x1f, + 0x7e, 0x77, 0x9c, 0x48, 0x45, 0x50, 0x9a, 0xcf, 0x40, 0x13, 0x59, 0x95, 0x15, 0xaf, 0xff, 0x0a, + 0xfc, 0x97, 0xdf, 0x2c, 0x82, 0x09, 0xaf, 0xe7, 0x77, 0x69, 0x08, 0xc1, 0x15, 0x6f, 0x1c, 0x16, + 0x66, 0xfc, 0x81, 0x89, 0x19, 0x8e, 0xd6, 0x89, 0x81, 0xb4, 0x2e, 0x31, 0x12, 0xc1, 0xa8, 0x7d, + 0xe7, 0xb3, 0xfd, 0x92, 0xf0, 0xf9, 0x7e, 0x49, 0xf8, 0xfb, 0x7e, 0x49, 0xf8, 0xe4, 0x59, 0x69, + 0xea, 0xf3, 0x67, 0xa5, 0xa9, 0xbf, 0x3c, 0x2b, 0x4d, 0xdd, 0x5f, 0x1f, 0xe4, 0xfb, 0xb1, 0xbe, + 0x4b, 0x91, 0xbe, 0xee, 0xb7, 0x94, 0x0f, 0xfa, 0x9e, 0x2a, 0x3d, 0x17, 0x91, 0x87, 0xb3, 0xec, + 0x07, 0xe9, 0xcb, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x5f, 0xab, 0xc1, 0xc7, 0x37, 0x1e, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2599,6 +2654,39 @@ func (m *Chain) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Denylist) > 0 { + for iNdEx := len(m.Denylist) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Denylist[iNdEx]) + copy(dAtA[i:], m.Denylist[iNdEx]) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Denylist[iNdEx]))) + i-- + dAtA[i] = 0x42 + } + } + if len(m.Allowlist) > 0 { + for iNdEx := len(m.Allowlist) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Allowlist[iNdEx]) + copy(dAtA[i:], m.Allowlist[iNdEx]) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Allowlist[iNdEx]))) + i-- + dAtA[i] = 0x3a + } + } + if m.ValidatorSetCap != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ValidatorSetCap)) + i-- + dAtA[i] = 0x30 + } + if m.ValidatorsPowerCap != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ValidatorsPowerCap)) + i-- + dAtA[i] = 0x28 + } + if m.MinPowerInTop_N != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.MinPowerInTop_N)) + i-- + dAtA[i] = 0x20 + } if m.Top_N != 0 { i = encodeVarintQuery(dAtA, i, uint64(m.Top_N)) i-- @@ -3518,6 +3606,27 @@ func (m *Chain) Size() (n int) { if m.Top_N != 0 { n += 1 + sovQuery(uint64(m.Top_N)) } + if m.MinPowerInTop_N != 0 { + n += 1 + sovQuery(uint64(m.MinPowerInTop_N)) + } + if m.ValidatorsPowerCap != 0 { + n += 1 + sovQuery(uint64(m.ValidatorsPowerCap)) + } + if m.ValidatorSetCap != 0 { + n += 1 + sovQuery(uint64(m.ValidatorSetCap)) + } + if len(m.Allowlist) > 0 { + for _, s := range m.Allowlist { + l = len(s) + n += 1 + l + sovQuery(uint64(l)) + } + } + if len(m.Denylist) > 0 { + for _, s := range m.Denylist { + l = len(s) + n += 1 + l + sovQuery(uint64(l)) + } + } return n } @@ -4537,6 +4646,127 @@ func (m *Chain) Unmarshal(dAtA []byte) error { break } } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MinPowerInTop_N", wireType) + } + m.MinPowerInTop_N = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MinPowerInTop_N |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorsPowerCap", wireType) + } + m.ValidatorsPowerCap = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ValidatorsPowerCap |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorSetCap", wireType) + } + m.ValidatorSetCap = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ValidatorSetCap |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Allowlist", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Allowlist = append(m.Allowlist, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denylist", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denylist = append(m.Denylist, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) From e3b6490553338089e312d314d07516828a614c5a Mon Sep 17 00:00:00 2001 From: Sergey <83376337+freak12techno@users.noreply.github.com> Date: Mon, 13 May 2024 18:18:56 +0300 Subject: [PATCH 023/102] chore: fixed all-pairs-valconsensus-address CLI command usage (#1870) --- x/ccv/provider/client/cli/query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ccv/provider/client/cli/query.go b/x/ccv/provider/client/cli/query.go index cf68d24681..f504a7557c 100644 --- a/x/ccv/provider/client/cli/query.go +++ b/x/ccv/provider/client/cli/query.go @@ -355,7 +355,7 @@ $ %s query provider registered-consumer-reward-denoms func CmdAllPairsValConAddrByConsumerChainID() *cobra.Command { cmd := &cobra.Command{ - Use: "all-pairs-valconsensus-address", + Use: "all-pairs-valconsensus-address [consumer-chain-id]", Short: "Query all pairs of valconsensus address by consumer chainId.", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { From c05de0e4fa84b05f84bafddf50fca30033c7f6cb Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Tue, 14 May 2024 11:24:07 +0200 Subject: [PATCH 024/102] chore: Try to make coderabbit ignore missing/extra newlines in md files (#1877) Update .coderabbit.yml --- .coderabbit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.coderabbit.yml b/.coderabbit.yml index b805c87ba2..e89dfcdfcd 100644 --- a/.coderabbit.yml +++ b/.coderabbit.yml @@ -23,7 +23,7 @@ reviews: "Assess the unit test code assessing sufficient code coverage for the changes associated in the pull request" - path: "**/*.md" instructions: | - "Assess the documentation for misspellings, grammatical errors, missing documentation and correctness" + "Assess the documentation for misspellings, grammatical errors, missing documentation and correctness. Please DO NOT report any missing or superfluous newlines, in particular at the end or beginning of files." - path: ".changelog/*" instructions: | "Assess the changes in the changelog for correctness and completeness, particularly flagging missing changes" From 1294f9040481935a91bc50a9af4fb08e31e7c3b8 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Tue, 14 May 2024 15:02:20 +0200 Subject: [PATCH 025/102] docs: Add PSS docs (#1859) * Add params to proposals * Start rewriting intro * Finish overview and terminology * Write up generics about PSS and power shaping * Add more info about top N and optin * Nit: apostrophe * Clarify governance proposal process for Opt In chains * Apply suggestions from code review Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Add missing newlines * Update docs/docs/features/partial-set-security.md Co-authored-by: insumity * Update docs/docs/features/power-shaping.md Co-authored-by: insumity * Use Interchain Security instead of ICSv2 * docs: Add PSS docs (Part 2) (#1861) * first version * first commit * one more warning on having all validators opt out * Update docs/docs/validators/partial-set-security-for-validators.md Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update docs/docs/validators/partial-set-security-for-validators.md Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update docs/docs/validators/partial-set-security-for-validators.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update docs/docs/validators/partial-set-security-for-validators.md Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update docs/docs/frequently-asked-questions.md Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update docs/docs/validators/partial-set-security-for-validators.md Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update docs/docs/frequently-asked-questions.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update docs/docs/validators/partial-set-security-for-validators.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * took into account some comments * small comment changes --------- Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Add warning that vals have to manually opt out if going out of top N * Add short PSS FAQ * Add FAQ on how many chains vals can opt in on * Change first to third person * Fix typo * Add missing comma * added a warning * Add more guidelines to 'how to choose the power shaping parameters' * Mention list-consumer-chains query * Add tip about default commission rate --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: insumity --- docs/docs/consumer-development/onboarding.md | 33 +++- docs/docs/features/partial-set-security.md | 28 ++++ docs/docs/features/power-shaping.md | 52 ++++++ docs/docs/features/proposals.md | 11 +- docs/docs/features/slashing.md | 3 + docs/docs/frequently-asked-questions.md | 68 +++++--- docs/docs/introduction/overview.md | 13 +- docs/docs/introduction/terminology.md | 6 +- .../partial-set-security-for-validators.md | 149 ++++++++++++++++++ 9 files changed, 329 insertions(+), 34 deletions(-) create mode 100644 docs/docs/features/partial-set-security.md create mode 100644 docs/docs/features/power-shaping.md create mode 100644 docs/docs/validators/partial-set-security-for-validators.md diff --git a/docs/docs/consumer-development/onboarding.md b/docs/docs/consumer-development/onboarding.md index f4f022c946..703fd56335 100644 --- a/docs/docs/consumer-development/onboarding.md +++ b/docs/docs/consumer-development/onboarding.md @@ -41,12 +41,15 @@ Additionally, reach out to the community via the [forum](https://forum.cosmos.ne - [ ] determine consumer chain parameters to be put in the proposal - [ ] take note to include a link to your onboarding repository - [ ] describe the purpose and benefits of running your chain +- [ ] determine whether your chain should be an Opt-In chain or a Top N chain (see [Partial Set Security](../features/partial-set-security.md)) +- [ ] if desired, decide on power-shaping parameters (see [Power Shaping](../features/power-shaping.md)) Example of a consumer chain addition proposal. ```js // ConsumerAdditionProposal is a governance proposal on the provider chain to spawn a new consumer chain. -// If it passes, then all validators on the provider chain are expected to validate the consumer chain at spawn time. +// If it passes, if the top_N parameter is not equal to 0, the top N% of validators by voting power on the provider chain are expected to validate the consumer chain at spawn time. +// Otherwise, only validators that opted in during the proposal period are expected to validate the consumer chain at spawn time. // It is recommended that spawn time occurs after the proposal end time. { // Title of the proposal @@ -69,7 +72,7 @@ Example of a consumer chain addition proposal. // Hash of the consumer chain binary that should be run by validators on chain initialization. // It is used for off-chain confirmation of binary validity by validators and other parties. "binary_hash": "376cdbd3a222a3d5c730c9637454cd4dd925e2f9e2e0d0f3702fc922928583f1", - // Time on the provider chain at which the consumer chain genesis is finalized and all validators + // Time on the provider chain at which the consumer chain genesis is finalized and validators // will be responsible for starting their consumer chain validator node. "spawn_time": "2023-02-28T20:40:00.000000Z", // Unbonding period for the consumer chain. @@ -97,13 +100,35 @@ Example of a consumer chain addition proposal. // Note that transfer_channel_id is the ID of the channel end on the consumer chain. // it is most relevant for chains performing a standalone to consumer changeover // in order to maintain the existing ibc transfer channel - "distribution_transmission_channel": "channel-123" + "distribution_transmission_channel": "channel-123", + // Corresponds to the percentage of validators that have to validate the chain under the Top N case. + // For example, 53 corresponds to a Top 53% chain, meaning that the top 53% provider validators by voting power + // have to validate the proposed consumer chain. top_N can either be 0 or any value in [50, 100]. + // A chain can join with top_N == 0 as an Opt In chain, or with top_N ∈ [50, 100] as a Top N chain. + "top_N": 95, + // Corresponds to the maximum power (percentage-wise) a validator can have on the consumer chain. For instance, if + // `validators_power_cap` is set to 32, it means that no validator can have more than 32% of the voting power on the + // consumer chain. Note that this might not be feasible. For example, think of a consumer chain with only + // 5 validators and with `validators_power_cap` set to 10%. In such a scenario, at least one validator would need + // to have more than 20% of the total voting power. Therefore, `validators_power_cap` operates on a best-effort basis. + "validators_power_cap": 0, + // Corresponds to the maximum number of validators that can validate a consumer chain. + // Only applicable to Opt In chains. Setting `validator_set_cap` on a Top N chain is a no-op. + "validator_set_cap": 0, + // Corresponds to a list of provider consensus addresses of validators that are the ONLY ones that can validate + // the consumer chain. + "allowlist": [], + // Corresponds to a list of provider consensus addresses of validators that CANNOT validate the consumer chain. + "denylist": [] } ``` ## 4. Launch -The consumer chain starts after at least 66.67% of all provider's voting power comes online. The consumer chain is considered interchain secured once the appropriate CCV channels are established and the first validator set update is propagated from the provider to the consumer +The consumer chain starts after at least 66.67% of its voting power comes online, +i.e. a Top N chain, the consumer chain starts after at least 66.67% of the `top_N`% of the provider chain's voting power comes online; +for an Opt-In chain, it starts after 66.67% of the validators that opted-in while the consumer chain was being proposed come online. +The consumer chain is considered interchain secured once the appropriate CCV channels are established and the first validator set update is propagated from the provider to the consumer - [ ] provide a repo with onboarding instructions for validators (it should already be listed in the proposal) - [ ] genesis.json with ccv data populated (MUST contain the initial validator set) diff --git a/docs/docs/features/partial-set-security.md b/docs/docs/features/partial-set-security.md new file mode 100644 index 0000000000..6f8cb3897e --- /dev/null +++ b/docs/docs/features/partial-set-security.md @@ -0,0 +1,28 @@ +--- +sidebar_position: 5 +--- + +# Partial Set Security + +Partial Set Security (PSS) allows consumer chains to leverage only a subset of validators from the provider chain, which offers more flexibility than the traditional Replicated Security model. By introducing the top_N parameter, each consumer chain can choose the extent of security needed: + + * Top N: Requires the top N% validators from the provider chain to secure the consumer chain. This guarantees that the validators with the most power on the provider will validate the consumer chain, while others can voluntarily opt in. + + * Opt-In: If the `top_N` parameter is set to zero, no validator is mandated to secure the consumer chain. Instead, any validator from the provider chain can opt in using a dedicated transaction. + +An advantage of a Top N chain is that the consumer chain is guaranteed to receive at least a certain fraction of the market cap of the provider chain in security. In turn, this chain needs to be approved by governance, since validators will be forced to run the chain. Thus, Top N chains should typically expect to need to provide a strong case for why they should be added to the provider chain, and they should make sure they offer enough rewards to incentivize validators and delegators to vote for their proposal. + +Opt-In chains, on the other hand, are more flexible. While for technical reasons, they are also currently added via governance proposals, since validators are never forced to validate these chains and simply opt in if they want to, they should typically expect to get their proposals approved much more easily compared to Top N chains, since validators that do not want to validate the chain can simply choose not to opt in. +However, opt in chains do not get a fixed amount of security as a relation of the market cap of the provider as top N chains do, so opt in chains might want to keep an eye on how many validators have opted in to validate their chain and adjust their reward emissions accordingly to incentivize validators. + +:::tip +Partial Set Security is handled only by the provider chain - the consumer chains are simply sent validator sets, and they are not aware that this represents only a subset of the provider chain's validator set. +::: + +:::caution +Both Opt In and Top N chains currently require a governance proposal to be added to the provider chain. + +For Top N chains, this is also the long term vision for how they are launched. + +For Opt In chains, this is a temporary measure to prevent issues around chain ID squatting. Eventually, the plan is to allow launching Opt In chains permissionlessly without going through governance, with quality control being handled by the market of validators deciding which chains they would like to validate on. +::: diff --git a/docs/docs/features/power-shaping.md b/docs/docs/features/power-shaping.md new file mode 100644 index 0000000000..44b7582c98 --- /dev/null +++ b/docs/docs/features/power-shaping.md @@ -0,0 +1,52 @@ +# Power Shaping + +To give consumer chains more flexibility in choosing their validator set, Interchain Security offers +several "power shaping" mechanisms for consumer chains. + +These are: +1) **Capping the size of the validator set**: The consumer chain can specify a maximum number of validators it +wants to have in its validator set. This can be used to limit the number of validators in the set, which can +be useful for chains that want to have a smaller validator set for faster blocks or lower overhead. +*Note*: This is only applicable to Opt In chains (chains with Top N = 0). +1) **Capping the fraction of power any single validator can have**: The consumer chain can specify a maximum fraction +of the total voting power that any single validator in its validator set should have. +This is a security measure with the intention of making it harder for a single large validator to take over a consumer chain. This mitigates the risk of an Opt In chain with only a few validators being dominated by a validator with a large amount of voting power opting in. +For example, setting this fraction to e.g. 33% would mean that no single validator can have more than 33% of the total voting power, +and thus there is no single validator who would stop the chain by going offline. +Note that this is a soft cap, and the actual power of a validator can exceed this fraction if the validator set is small (e.g. there are only 3 validators and the cap is 20%). +1) **Allowlist and denylist**: The consumer chain can specify a list of validators that are allowed or disallowed from participating in the validator set. If an allowlist is set, all validators not on the allowlist cannot validate the consumer chain. If a validator is on both lists, the denylist takes precedence, that is, they cannot validate the consumer chain. If neither list is set, all validators are able to validate the consumer chain. + +:::warning +Note that if denylisting is used in a Top N consumer chain, then the chain might not be secured by N% of the total provider's +power. For example, consider that the top validator `V` on the provider chain has 10% of the voting power, and we have a Top 50% consumer chain, +then if `V` is denylisted, the consumer chain would only be secured by at least 40% of the provider's power. +::: + +All these mechanisms are set by the consumer chain in the `ConsumerAdditionProposal`. They operate *solely on the provider chain*, meaning the consumer chain simply receives the validator set after these rules have been applied and does not have any knowledge about whether they are applied. + +Each of these mechanisms is *set during the consumer addition proposal* (see [Onboarding](../consumer-development/onboarding.md#3-submit-a-governance-proposal)), and is currently *immutable* after the chain has been added. + +The values can be seen by querying the list of consumer chains: +```bash +interchain-security-pd query provider list-consumer-chains +``` + +## Guidelines for setting power shaping parameters + +When setting power shaping parameters, please consider the following guidelines: +* Do not cap the validator set size too low: Notice that this number is the **maximum* number of validators that will ever validate the consumer chain. If this number is too low, the chain will be very limited in the +amount of stake that secures it. The validator set size cap should only be used if there are strong reasons to prefer fewer validators. Consider that setting the cap will mean that +even if the whole validator set of the provider wants to validate on the chain, some validators will simply not be able to. +* Capping the fraction of power any single validator can have is a decent security measure, but it's good to be aware of the interactions with the size of the validator set. +For example, if there are only 3 validators, and the cap is 20%, this will not be possible (since even splitting the power fairly would mean that each validator has 33% of the power, so is above the cap). +However, the cap can be a good measure to prevent a single large validator from essentially taking over the chain. +In general, values under 33% make sense (since a validator that has 33% of the chains power would halt the chain if they go offline). +Notice that the smaller this value is, the more the original voting power gets distorted, which could discourage large validators from deciding to opt in to the chain. +* If the allowlist is *empty*, all validators can validate the chain. If it is *non empty*, then *only* validators on the allowlist can validate the chain. +Thus, an allowlist containing too few validators is a security risk. In particular, consider that if the validators on the allowlist lose a lot of stake or stop being validators, +an allowlist that is too short can very quickly become outdated and leave too few validators, or validators with too little stake, to secure the chain in a decentralized way. +* If the denylist is too full, this can likewise be problematic. If too many large validators are denylisted, the chain might not be secured by a large enough fraction of the provider's power, in particular when +the power distribution on the provider shifts and the denylisted validators gain more power. + +In general, when setting these parameters, consider that the voting power distribution in the future might be very different from the one right now, +and that the chain should be secure even if the power distribution changes significantly. \ No newline at end of file diff --git a/docs/docs/features/proposals.md b/docs/docs/features/proposals.md index f2d2e3d4b2..2343f019ae 100644 --- a/docs/docs/features/proposals.md +++ b/docs/docs/features/proposals.md @@ -42,10 +42,13 @@ Minimal example: "blocks_per_distribution_transmission": 1000, "historical_entries": 10000, "genesis_hash": "d86d756e10118e66e6805e9cc476949da2e750098fcc7634fd0cc77f57a0b2b0", - "binary_hash": "376cdbd3a222a3d5c730c9637454cd4dd925e2f9e2e0d0f3702fc922928583f1" - // relevant for chains performing a standalone to consumer changeover - // in order to maintain the existing ibc transfer channel - "distribution_transmission_channel": "channel-123" + "binary_hash": "376cdbd3a222a3d5c730c9637454cd4dd925e2f9e2e0d0f3702fc922928583f1", + "distribution_transmission_channel": "channel-123", + "top_N": 95, + "validators_power_cap": 0, + "validator_set_cap": 0, + "allowlist": [], + "denylist": [] } ``` More examples can be found in the interchain security testnet repository [here](https://github.com/cosmos/testnets/blob/master/interchain-security/stopped/baryon-1/proposal-baryon-1.json) and [here](https://github.com/cosmos/testnets/blob/master/interchain-security/stopped/noble-1/start-proposal-noble-1.json). diff --git a/docs/docs/features/slashing.md b/docs/docs/features/slashing.md index 6db4c3ee0e..718c3cbad0 100644 --- a/docs/docs/features/slashing.md +++ b/docs/docs/features/slashing.md @@ -21,6 +21,9 @@ Instead of slashing, the provider will only jail offending validator for the dur [Slash throttling](../adrs/adr-002-throttle.md) (sometimes called jail throttling) mechanism ensures that only a fraction of the validator set can be jailed at any one time to prevent malicious consumer chains from harming the provider. ::: +Note that validators are only jailed for downtime on consumer chains that they opted-in to validate on, +or in the case of Top N chains, where they are automatically opted in by being in the Top N% of the validator set on the provider. + ## Equivocation Infractions Equivocation infractions are reported by external agents (e.g., relayers) that can submit to the provider evidence of light client or double signing attacks observed on a consumer chain. diff --git a/docs/docs/frequently-asked-questions.md b/docs/docs/frequently-asked-questions.md index ce76e4e24d..e31123a9cf 100644 --- a/docs/docs/frequently-asked-questions.md +++ b/docs/docs/frequently-asked-questions.md @@ -4,13 +4,9 @@ title: "Frequently Asked Questions" slug: /faq --- -## What is the meaning of Validator Set Replication? - -VSR simply means that the same validator set is used to secure both the provider and consumer chains. VSR is ensured through ICS protocol which keeps consumers up to date with the validator set of the provider. - ## What is a consumer chain? -Consumer chain is blockchain operated by the same validator operators as the provider chain. The ICS protocol ensures the validator set replication properties (informs consumer chain about the current state of the validator set on the provider) +Consumer chain is a blockchain operated by (a subset of) the validators of the provider chain. The ICS protocol ensures that the consumer chain gets information about which validators should run it (informs consumer chain about the current state of the validator set and the opted in validators for this consumer chain on the provider). Consumer chains are run on infrastructure (virtual or physical machines) distinct from the provider, have their own configurations and operating requirements. @@ -29,7 +25,7 @@ At the very least, the consumer chain could replace the validator set, remove th ## What happens to provider if consumer is down? Consumer chains do not impact the provider chain. -The ICS protocol is concerned only with validator set replication and the only communication that the provider requires from the consumer is information about validator activity (essentially keeping the provider informed about slash events). +The ICS protocol is concerned only with validator set management, and the only communication that the provider requires from the consumer is information about validator activity (essentially keeping the provider informed about slash events). ## Can I run the provider and consumer chains on the same machine? @@ -66,22 +62,15 @@ Validators can also be representatives but representatives are not required to r This feature discerns between validator operators (infrastructure) and governance representatives which further democratizes the ecosystem. This also reduces the pressure on validators to be involved in on-chain governance. -## Can validators opt-out of replicated security? - -At present, the validators cannot opt-out of validating consumer chains. +## Can validators opt out of validating a consumer chain? -There are multiple opt-out mechanisms under active research. +A validator can always opt out from an Opt-In consumer chain. +A validator can only opt out from a Top N chain if the validator does not belong to the top N% validators. -## How does Equivocation Governance Slashing work? +## How does Slashing work? -To avoid potential attacks directed at provider chain validators, a new mechanism was introduced: - -When a validator double-signs on the consumer chain, a special type of slash packet is relayed to the provider chain. The provider will store information about the double signing validator and allow a governance proposal to be submitted. -If the double-signing proposal passes, the offending validator will be slashed on the provider chain and tombstoned. Tombstoning will permanently exclude the validator from the active set of the provider. - -:::caution -An equivocation proposal cannot be submitted for a validator that did not double sign on any of the consumer chains. -::: +Validators that perform an equivocation or a light-client attack on a consumer chain are slashed on the provider chain. +We achieve this by submitting the proof of the equivocation or the light-client attack to the provider chain (see [slashing](features/slashing.md)). ## Can Consumer Chains perform Software Upgrades? @@ -102,8 +91,47 @@ To become a consumer chain use this [checklist](./consumer-development/onboardin Currently supported versions: - Hermes 1.4.1 -- Support for the CCV module was added to the Go [relayer](https://github.com/cosmos/relayer) in v2.2.0 but v2.4.0 has significant performance fixes which makes it the earliest suggested version to use. ## How does key delegation work in ICS? You can check the [Key Assignment Guide](./features/key-assignment.md) for specific instructions. + +## How does Partial Set Security work? + +Partial Set Security allows a provider chain to share only a subset of its validator set with a consumer chain. This subset can be determined by the top N% validators by voting power, or by validators opting in to validate the consumer chain. Partial Set Security allows for flexible tradeoffs between security, decentralization, and the budget a consumer chain spends on rewards to validators. + +See the [Partial Set Security](./features/partial-set-security.md) section for more information. + +## How does a validator know which consumers chains it has to validate? + +In order for a validator to keep track of all the chains it has to validate, the validator can use the +[`has-to-validate` query](validators/partial-set-security-for-validators.md#which-chains-does-a-validator-have-to-validate). + +## How many chains can a validator opt in to? + +There is **no** limit in the number of consumers chains a validator can choose to opt in to. + +## Can validators assign a consensus keys while a consumer-addition proposal is in voting period? +Yes, see the [Key Assignment Guide](./features/key-assignment.md) for more information. + +## Can validators assign a consensus key during the voting period for a consumer-addition proposal if they are not in the top N? +Yes. + +## Can validators opt in to an Opt-in chain after its consumer-addition proposal voting period is over but before the spawn time? +Yes. + +## Can validators opt in to an Opt-in chain after the spawn time if nobody else opted in? +No, the consumer chain will not be added if nobody opted in by the spawn time. At least one validator, regardless of its voting power, must opt in before the spawn time arrives in order for the chain can start. + +## Can all validators opt out of an Opt-in chain? +Yes, the consumer chain will halt with an ERR CONSENSUS FAILURE error after the opt-out message for the last validator is received. + +## Can validators set a commission rate for chains they have not opted in to? +Yes, and this is useful for validators that are not in the top N% of the provider chain, but might move into the top N% in the future. +By setting the commission rate ahead of time, they can make sure that they immediately have a commission rate of their choosing as soon as they are in the top N%. + +## On how many consumer chains can a validator opt-in at the same time? + +There is no limit to the number of consumer chains a validator can opt in to. +However, some consumer chains may restrict the validators actually getting to validate there, for example consumer chains can set up denylists to stop certain validators from validating there. +See the [Power Shaping](./features/power-shaping.md) section for more information. diff --git a/docs/docs/introduction/overview.md b/docs/docs/introduction/overview.md index aba31751e1..b9f870bfab 100644 --- a/docs/docs/introduction/overview.md +++ b/docs/docs/introduction/overview.md @@ -4,16 +4,19 @@ sidebar_position: 1 # Overview :::info -Replicated security (aka interchain security V1) is an open sourced IBC application which allows cosmos blockchains to lease their proof-of-stake security to one another. +Interchain Security is an open sourced IBC application which allows cosmos blockchains to lease their proof-of-stake security to one another.

-Replicated security allows anyone to launch a "consumer" blockchain using the same validator set as the "provider" blockchain by creating a governance proposal. If the proposal is accepted, provider chain validators start validating the consumer chain as well. Consumer chains will therefore inherit the full security and decentralization of the provider. +Interchain Security allows anyone to launch a "consumer" blockchain using a subset, or even the entire, validator set from the "provider" blockchain by creating a governance proposal. If the proposal is accepted, provider chain validators start validating the consumer chain as well. Consumer chains will therefore inherit security and decentralization from the provider. ::: -## Why Replicated Security? -- Full provider security. At launch, consumer chains are secured by the full validator set and market cap of the provider chain. + + +## Why Interchain Security? + +- The right amount of security for each application. Consumer chains can choose to inherit the whole validator set from the provider, or they can launch as an opt in chain where only a subset of the provider validators validate the consumer chain. This allows for a wide range of security tradeoffs. - Independent block-space. Transactions on consumer chains do not compete with any other applications. This means that there will be no unexpected congestion, and performance will generally be much better than on a shared smart contract platform such as Ethereum. - Projects keep majority of gas fees. Depending on configuration, these fees either go to the project’s community DAO, or can be used in the protocol in other ways. -- No validator search. Consumer chains do not have their own validator sets, and so do not need to find validators one by one. A governance vote will take place for a chain to get adopted by the provider validators which will encourage participation and signal strong buy-in into the project's long-term success. +- No validator search. Consumer chains do not have their own validator sets, and so do not need to find validators one by one. Validators from the provider chain validate on the consumer chain with their stake on the provider chain, earning additional rewards. For the consumer chain, this comes with the benefit of exposing their chain to the wider audience of the provider chain. - Instant sovereignty. Consumers can run arbitrary app logic similar to standalone chains. At any time in the future, a consumer chain can elect to become a completely standalone chain, with its own validator set. ## Core protocol diff --git a/docs/docs/introduction/terminology.md b/docs/docs/introduction/terminology.md index fd06174f92..dd9cd57602 100644 --- a/docs/docs/introduction/terminology.md +++ b/docs/docs/introduction/terminology.md @@ -16,7 +16,11 @@ Interchain Security is the Cosmos-specific category of Shared Security that uses ## Replicated Security -A particular protocol/implementation of Interchain Security that fully replicates the security and decentralization of a validator set across multiple blockchains. Replicated security has also been referred to as "Cross Chain Validation" or "Interchain Security V1", a legacy term for the same protocol. That is, a "provider chain" such as the Cosmos Hub can share its exact validator set with multiple consumer chains by communicating changes in its validator set over IBC. Note this documentation is focused on explaining the concepts from replicated security. +A particular protocol/implementation of Interchain Security that fully replicates the security and decentralization of a validator set across multiple blockchains. Replicated security has also been referred to as "Cross Chain Validation" or "Interchain Security V1", a legacy term for the same protocol. That is, a "provider chain" such as the Cosmos Hub can share its exact validator set with multiple consumer chains by communicating changes in its validator set over IBC. + +## Partial Set Security + +A major iteration of Interchain Security, also known as "Interchain Security V2". Partial Set Security allows a provider chain to share only a subset of its validator set with a consumer chain. This subset can be determined by the top N% validators by voting power, or by validators opting in to validate the consumer chain. Partial Set Security allows for more flexible security tradeoffs than Replicated Security. ## Mesh security diff --git a/docs/docs/validators/partial-set-security-for-validators.md b/docs/docs/validators/partial-set-security-for-validators.md new file mode 100644 index 0000000000..0d2fce211e --- /dev/null +++ b/docs/docs/validators/partial-set-security-for-validators.md @@ -0,0 +1,149 @@ +--- +sidebar_position: 6 +--- + +# Partial Set Security + +[Partial Set Security](../features/partial-set-security.md) allows consumer chains to join as Opt-In or Top N. +Here, we show how a validator can opt in, opt out, or set a custom commission rate on a consumer chain, as well +as useful queries that a validator can use to figure out which chains it has to validate, etc. + +## Messages +### How to opt in to a consumer chain? + +:::warning +A validator is automatically opted in to a Top N chain if the validator belongs to the top N% of the validators on the provider chain. +::: + +In a Top N chain, a validator that does not belong to the top N% of the validators on the provider can still choose +to opt in to a consumer chain. In other words, validators can opt in, in both Opt-In and Top N chains. + +A validator can opt in to a consumer chain by issuing the following message: +```bash +interchain-security-pd tx provider opt-in +``` + +where +- `consumer-chain-id` is the string identifier of the consumer chain the validator wants to opt in to; +- `consumer-pub-key` corresponds to the public key the validator wants to use on the consumer chain, and it has the +following format `{"@type":"/cosmos.crypto.ed25519.PubKey","key":""}`. + +A validator can opt in to an existing consumer chain that is already running, or to a [proposed](../features/proposals.md) +consumer chain that is still being voted on. A validator can use the following command to retrieve the currently existing +consumer chains: +```bash +interchain-security-pd query provider list-consumer-chains +``` +and this command to see the currently proposed consumer chains: +```bash +interchain-security-pd query provider list-proposed-consumer-chains +``` + + +:::tip +By setting the `consumer-pub-key`, a validator can both opt in to a chain and assign a +public key on a consumer chain. Note that a validator can always [assign](../features/key-assignment.md) +a new consumer key at a later stage. The key-assignment [rules](../features/key-assignment.md#rules) +still apply when setting `consumer-pub-key` when opting in. +::: + +:::info +A validator is only eligible for consumer rewards from a consumer chain if the validator is opted into that chain. +::: + +### How to opt out from a consumer chain? +A validator can opt out from a consumer by issuing the following message: + +```bash +interchain-security-pd tx provider opt-out +``` +where +- `consumer-chain-id` is the string identifier of the consumer chain. + +:::warning +A validator cannot opt out from a Top N chain if it belongs to the top N% validators of the provider. +::: + +:::warning +If a validator moves from the Top N to outside of the top N% of the validators on the provider, it will **not** +be automatically opted-out. The validator has to manually opt out. +::: + +:::warning +A validator can stop its node on a consumer chain **only** after opting out and confirming through the `has-to-validate` +query (see [below](./partial-set-security-for-validators.md#which-chains-does-a-validator-have-to-validate)) that it does +not have to validate the consumer chain any longer. +::: + +:::warning +If all validators opt out from an Opt-In chain, the chain will halt with a consensus failure upon receiving the VSCPacket with an empty validator set. +::: + +### How to set specific per consumer chain commission rate? +A validator can choose to set a different commission rate on each of the consumer chains it validates. +This can be done with the following command: +```bash +interchain-security-pd tx provider set-consumer-commission-rate +``` +where + +- `consumer-chain-id` is the string identifier of the consumer chain; +- `comission-rate` decimal in `[minRate, 1]` where `minRate` corresponds to the minimum commission rate set on the +provider chain (see `min_commission_rate` in `interchain-security-pd query staking params`). + +'''tip +If a validator does not set a commission rate on a consumer chain, the commission rate defaults to their commission rate on the provider chain. +''' + + +## Queries +Partial Set Security introduces a number of queries to assist validators determine which consumer chains they have to +validate, their commission rate per chain, etc. + +### Which chains does a validator have to validate? +Naturally, a validator is aware of the Opt-In chains it has to validate because in order to validate an Opt-In chain, +a validator has to manually opt in to the chain. This is not the case for Top N chains where a validator might be required +to validate such a chain without explicitly opting in if it belongs to the top N% of the validators on the provider. + +We introduce the following query: +```bash +interchain-security-pd query provider has-to-validate +``` +that can be used by validator with `provider-validator-address` address to retrieve the list of chains that it has to validate. + + +:::tip +As a validator, the list of chains returned by `has-to-validate` is the list of chains you **should** be validating to avoid +getting jailed for downtime. +::: + +### How do you know how much voting power you need to have to be in the top N for a chain? +This can be seen as part of the `list-consumer-chains` query: +```bash +interchain-security-pd query provider list-consumer-chains +``` +where the `min_power_in_top_N` field shows the minimum voting power required to be +automatically opted in to the chain. + +:::tip +`list-consumer-chains` shows the minimal voting power *right now*, but +the automatic opt-in happens only when epochs end on the provider. +In consequence, a validators power might be large enough to be automatically opted in +during an epoch, but if their power is sufficiently decreased before the epoch ends, +they will not be opted in automatically. +::: + + +### How to get all the opted-in validators on a consumer chain? +With the following query: +```bash +interchain-security-pd query provider consumer-opted-in-validators +``` +we can see all the opted-in validators on `consumer-chain-id` that were manually or automatically opted in. + +### How can we see the commission rate a validator has set on a consumer chain? +Using the following query: +```bash +interchain-security-pd query provider validator-consumer-commission-rate +``` +we retrieve the commission rate set by validator with `provider-validator-address` address on `consumer-chain-id`. \ No newline at end of file From 0b24c305a59152128e5a96f009245a1378e8298a Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Thu, 16 May 2024 15:47:27 +0200 Subject: [PATCH 026/102] docs: Minor improvements (#1882) * Fix typo in tip * Minor improvements around commission rate * Mention soft opt-out * Link to normal consumer addition prop * Remove confusing line from changeover prop * Remove instructions about not assigning keys * Clarify starting condition for top N chains * Incorporate comments * Update docs/docs/validators/joining-testnet.md Co-authored-by: insumity * Reformulate opting out --------- Co-authored-by: insumity --- docs/docs/consumer-development/app-integration.md | 3 ++- .../consumer-development/changeover-procedure.md | 3 +-- docs/docs/consumer-development/onboarding.md | 5 ++--- docs/docs/features/key-assignment.md | 5 +++++ docs/docs/features/partial-set-security.md | 2 +- docs/docs/features/power-shaping.md | 14 +++++++++++--- docs/docs/features/proposals.md | 7 +++++++ docs/docs/frequently-asked-questions.md | 10 ++-------- docs/docs/validators/joining-testnet.md | 2 +- .../partial-set-security-for-validators.md | 15 ++++++++++++--- 10 files changed, 44 insertions(+), 22 deletions(-) diff --git a/docs/docs/consumer-development/app-integration.md b/docs/docs/consumer-development/app-integration.md index 81d9141242..73c034d073 100644 --- a/docs/docs/consumer-development/app-integration.md +++ b/docs/docs/consumer-development/app-integration.md @@ -28,5 +28,6 @@ This allows the consumer chain to leverage those modules while also using the `x With these modules enabled, the consumer chain can mint its own governance tokens, which can then be delegated to prominent community members which are referred to as "representatives" (as opposed to "validators" in standalone chains). The token may have different use cases besides just voting on governance proposals. ## Standalone chain to consumer chain changeover -This feature is being actively worked on. Information will be provided at a later time. + +See the [standalone chain to consumer chain changeover guide](./changeover-procedure.md) for more information on how to transition your standalone chain to a consumer chain. diff --git a/docs/docs/consumer-development/changeover-procedure.md b/docs/docs/consumer-development/changeover-procedure.md index 0a3954283a..332bade599 100644 --- a/docs/docs/consumer-development/changeover-procedure.md +++ b/docs/docs/consumer-development/changeover-procedure.md @@ -150,7 +150,7 @@ Additionally, reach out to the community via the [forum](https://forum.cosmos.ne - [ ] determine consumer chain parameters to be put in the proposal - [ ] take note to include a link to your onboarding repository -Example of a consumer chain addition proposal. +Example of a consumer chain addition proposal (compare with the [ConsumerAdditionProposal](./onboarding.md#3-submit-a-governance-proposal) in the ICS Provider Proposals section for chains that *launch* as consumers): ```js // ConsumerAdditionProposal is a governance proposal on the provider chain to spawn a new consumer chain or add a standalone chain. @@ -167,7 +167,6 @@ Example of a consumer chain addition proposal. // Must be unique from all other consumer chain ids of the executing provider chain. "chain_id": "standalone-1", // Initial height of new consumer chain. - // For a completely new chain, this will be {0,1}. "initial_height" : { // must correspond to current revision number of standalone chain // e.g. standalone-1 => "revision_number": 1 diff --git a/docs/docs/consumer-development/onboarding.md b/docs/docs/consumer-development/onboarding.md index 703fd56335..9f438dd8d4 100644 --- a/docs/docs/consumer-development/onboarding.md +++ b/docs/docs/consumer-development/onboarding.md @@ -125,9 +125,8 @@ Example of a consumer chain addition proposal. ## 4. Launch -The consumer chain starts after at least 66.67% of its voting power comes online, -i.e. a Top N chain, the consumer chain starts after at least 66.67% of the `top_N`% of the provider chain's voting power comes online; -for an Opt-In chain, it starts after 66.67% of the validators that opted-in while the consumer chain was being proposed come online. +The consumer chain starts after at least 66.67% of its voting power comes online. +Note that this means 66.67% of the voting power in the *consumer* validator set, which will be comprised of all validators that either opted in to the chain or are part of the top N% of the provider chain (and are thus automatically opted in). The consumer chain is considered interchain secured once the appropriate CCV channels are established and the first validator set update is propagated from the provider to the consumer - [ ] provide a repo with onboarding instructions for validators (it should already be listed in the proposal) diff --git a/docs/docs/features/key-assignment.md b/docs/docs/features/key-assignment.md index 1906eb8b78..85dcb8e074 100644 --- a/docs/docs/features/key-assignment.md +++ b/docs/docs/features/key-assignment.md @@ -81,6 +81,11 @@ To change your key, simply repeat all of the steps listed above. Take note that To remove a key, simply switch it back to the consensus key you have assigned on the provider chain by following steps in the `Adding a key` section and using your provider consensus key. +:::warning +Validators are strongly recommended to assign a separate key for each consumer chain +and **not** reuse the provider key across consumer chains for security reasons. +::: + ## Querying proposed consumer chains To query the consumer addition proposals that are in the voting period, you can use the following command on the provider: diff --git a/docs/docs/features/partial-set-security.md b/docs/docs/features/partial-set-security.md index 6f8cb3897e..a2edbbb15d 100644 --- a/docs/docs/features/partial-set-security.md +++ b/docs/docs/features/partial-set-security.md @@ -24,5 +24,5 @@ Both Opt In and Top N chains currently require a governance proposal to be added For Top N chains, this is also the long term vision for how they are launched. -For Opt In chains, this is a temporary measure to prevent issues around chain ID squatting. Eventually, the plan is to allow launching Opt In chains permissionlessly without going through governance, with quality control being handled by the market of validators deciding which chains they would like to validate on. +For Opt In chains, this is a temporary measure to prevent issues around chain ID squatting, i.e. someone could spuriously register many desirable chain IDs of upcoming consumer chain and simply deny legitimate consumer chains from using them. Eventually, the plan is to allow launching Opt In chains permissionlessly without going through governance, with quality control being handled by the market of validators deciding which chains they would like to validate on. ::: diff --git a/docs/docs/features/power-shaping.md b/docs/docs/features/power-shaping.md index 44b7582c98..9884c6184b 100644 --- a/docs/docs/features/power-shaping.md +++ b/docs/docs/features/power-shaping.md @@ -7,13 +7,21 @@ These are: 1) **Capping the size of the validator set**: The consumer chain can specify a maximum number of validators it wants to have in its validator set. This can be used to limit the number of validators in the set, which can be useful for chains that want to have a smaller validator set for faster blocks or lower overhead. -*Note*: This is only applicable to Opt In chains (chains with Top N = 0). +:::info +This is only applicable to Opt In chains (chains with Top N = 0). +::: 1) **Capping the fraction of power any single validator can have**: The consumer chain can specify a maximum fraction of the total voting power that any single validator in its validator set should have. -This is a security measure with the intention of making it harder for a single large validator to take over a consumer chain. This mitigates the risk of an Opt In chain with only a few validators being dominated by a validator with a large amount of voting power opting in. +This is a security measure with the intention of making it harder for a single large validator to take over a consumer chain. This mitigates the risk of an Opt In chain with only a few validators being dominated by a validator with a large amount of stake opting in. For example, setting this fraction to e.g. 33% would mean that no single validator can have more than 33% of the total voting power, and thus there is no single validator who would stop the chain by going offline. -Note that this is a soft cap, and the actual power of a validator can exceed this fraction if the validator set is small (e.g. there are only 3 validators and the cap is 20%). +:::info +This is a soft cap, and the actual power of a validator can exceed this fraction if the validator set is small (e.g. there are only 3 validators and the cap is 20%). +::: +:::info +Rewards are distributed proportionally to validators with respect to their capped voting power on the consumer, +not their total voting power on the provider. +::: 1) **Allowlist and denylist**: The consumer chain can specify a list of validators that are allowed or disallowed from participating in the validator set. If an allowlist is set, all validators not on the allowlist cannot validate the consumer chain. If a validator is on both lists, the denylist takes precedence, that is, they cannot validate the consumer chain. If neither list is set, all validators are able to validate the consumer chain. :::warning diff --git a/docs/docs/features/proposals.md b/docs/docs/features/proposals.md index 2343f019ae..1f188b8567 100644 --- a/docs/docs/features/proposals.md +++ b/docs/docs/features/proposals.md @@ -77,6 +77,13 @@ Minimal example: } ``` +:::warning +Before the introduction of Partial Set Security, consumer chains typically included a "soft opt-out mechanism" +which allows the bottom N% of the provider's validators to not validate the consumer chain, without being jailed for downtime on the provider. +After the introduction of Partial Set Security, the use of the soft opt-out mechanism is discouraged, and consumer chains are +encouraged to use the topN parameter to not force validators with little stake to validate the chain. +::: + ## ChangeRewardDenomProposal Proposal type used to mutate the set of denoms accepted by the provider as rewards. diff --git a/docs/docs/frequently-asked-questions.md b/docs/docs/frequently-asked-questions.md index e31123a9cf..f648f8eac6 100644 --- a/docs/docs/frequently-asked-questions.md +++ b/docs/docs/frequently-asked-questions.md @@ -90,7 +90,7 @@ To become a consumer chain use this [checklist](./consumer-development/onboardin Currently supported versions: -- Hermes 1.4.1 +- Hermes 1.8.0 ## How does key delegation work in ICS? @@ -117,7 +117,7 @@ Yes, see the [Key Assignment Guide](./features/key-assignment.md) for more infor ## Can validators assign a consensus key during the voting period for a consumer-addition proposal if they are not in the top N? Yes. -## Can validators opt in to an Opt-in chain after its consumer-addition proposal voting period is over but before the spawn time? +## Can validators opt in to an Opt-in or Top N chain after its consumer-addition proposal voting period is over but before the spawn time? Yes. ## Can validators opt in to an Opt-in chain after the spawn time if nobody else opted in? @@ -129,9 +129,3 @@ Yes, the consumer chain will halt with an ERR CONSENSUS FAILURE error after the ## Can validators set a commission rate for chains they have not opted in to? Yes, and this is useful for validators that are not in the top N% of the provider chain, but might move into the top N% in the future. By setting the commission rate ahead of time, they can make sure that they immediately have a commission rate of their choosing as soon as they are in the top N%. - -## On how many consumer chains can a validator opt-in at the same time? - -There is no limit to the number of consumer chains a validator can opt in to. -However, some consumer chains may restrict the validators actually getting to validate there, for example consumer chains can set up denylists to stop certain validators from validating there. -See the [Power Shaping](./features/power-shaping.md) section for more information. diff --git a/docs/docs/validators/joining-testnet.md b/docs/docs/validators/joining-testnet.md index 09737cfb85..86a3dc2693 100644 --- a/docs/docs/validators/joining-testnet.md +++ b/docs/docs/validators/joining-testnet.md @@ -133,7 +133,7 @@ Additional scripts to setup your nodes are available [here](https://github.com/c :::tip Once you reach the active set on the provider chain, you will be required to validate all available consumer chains. -You can use the same consensus key on all consumer chains, or opt to use a different key on each consumer chain. +We strongly recommend that you assign a separate key for each consumer chain. Check out this [guide](../features/key-assignment.md) to learn more about key assignment in interchain security. ::: diff --git a/docs/docs/validators/partial-set-security-for-validators.md b/docs/docs/validators/partial-set-security-for-validators.md index 0d2fce211e..770f423a01 100644 --- a/docs/docs/validators/partial-set-security-for-validators.md +++ b/docs/docs/validators/partial-set-security-for-validators.md @@ -80,7 +80,7 @@ If all validators opt out from an Opt-In chain, the chain will halt with a conse ::: ### How to set specific per consumer chain commission rate? -A validator can choose to set a different commission rate on each of the consumer chains it validates. +A validator can choose to set a different commission rate on each of the consumer chains. This can be done with the following command: ```bash interchain-security-pd tx provider set-consumer-commission-rate @@ -91,9 +91,18 @@ where - `comission-rate` decimal in `[minRate, 1]` where `minRate` corresponds to the minimum commission rate set on the provider chain (see `min_commission_rate` in `interchain-security-pd query staking params`). -'''tip + If a validator does not set a commission rate on a consumer chain, the commission rate defaults to their commission rate on the provider chain. -''' + +:::tip +Validators can set their commission rate even for consumer chains that they are not currently opted in on, and the commission rate will be applied when they opt in. This is particularly useful for Top N chains, where validators might be opted in automatically, +so validators can set the commission rate in advance. +::: + +:::tip +If a validator opts out and then back in, this will *not* reset their commission rate back to the default. Instead, their +set commission rate still applies. +::: ## Queries From 04afa78ab3b633a5f5de9acab9a3f8d728a7c3ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 May 2024 16:54:37 +0200 Subject: [PATCH 027/102] build(deps): bump google.golang.org/protobuf from 1.33.0 to 1.34.1 (#1862) Bumps google.golang.org/protobuf from 1.33.0 to 1.34.1. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index db8b94586c..ffc4822264 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( golang.org/x/sys v0.18.0 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/grpc v1.63.2 - google.golang.org/protobuf v1.33.0 + google.golang.org/protobuf v1.34.1 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index ba33844aa1..a41e43b3d6 100644 --- a/go.sum +++ b/go.sum @@ -1662,8 +1662,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ 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.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From e1b191a893ca1a5c43d35a5eee26188efabd510f Mon Sep 17 00:00:00 2001 From: insumity Date: Fri, 17 May 2024 10:47:08 +0200 Subject: [PATCH 028/102] feat: add a query to retrieve validator set that was last sent to the consumer chain (#1867) * init commit * took into account comments * add docs * Update docs/docs/validators/partial-set-security-for-validators.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- ...query-for-latest-consumer-validator-set.md | 2 + .../partial-set-security-for-validators.md | 19 +- .../ccv/provider/v1/query.proto | 26 +- x/ccv/provider/client/cli/query.go | 36 + x/ccv/provider/keeper/grpc_query.go | 32 + x/ccv/provider/keeper/grpc_query_test.go | 146 +++ x/ccv/provider/types/query.pb.go | 940 +++++++++++++++--- x/ccv/provider/types/query.pb.gw.go | 101 ++ 8 files changed, 1163 insertions(+), 139 deletions(-) create mode 100644 .changelog/unreleased/features/provider/1867-add-query-for-latest-consumer-validator-set.md diff --git a/.changelog/unreleased/features/provider/1867-add-query-for-latest-consumer-validator-set.md b/.changelog/unreleased/features/provider/1867-add-query-for-latest-consumer-validator-set.md new file mode 100644 index 0000000000..dc33ef69d0 --- /dev/null +++ b/.changelog/unreleased/features/provider/1867-add-query-for-latest-consumer-validator-set.md @@ -0,0 +1,2 @@ +- Introduces the `consumer-validators` query to retrieve the latest set consumer-validator set for a consumer chain. + ([\#1863](https://github.com/cosmos/interchain-security/pull/1867)) diff --git a/docs/docs/validators/partial-set-security-for-validators.md b/docs/docs/validators/partial-set-security-for-validators.md index 770f423a01..2d6b2dd4a2 100644 --- a/docs/docs/validators/partial-set-security-for-validators.md +++ b/docs/docs/validators/partial-set-security-for-validators.md @@ -143,13 +143,30 @@ they will not be opted in automatically. ::: -### How to get all the opted-in validators on a consumer chain? +### How to retrieve all the opted-in validators on a consumer chain? With the following query: ```bash interchain-security-pd query provider consumer-opted-in-validators ``` we can see all the opted-in validators on `consumer-chain-id` that were manually or automatically opted in. +### How to retrieve all the consumer validators on a consumer chain? +With the following query: +```bash +interchain-security-pd query provider consumer-validators +``` +we can see all the _consumer validators_ (i.e., validator set) of `consumer-chain-id`. The consumer validators are the +ones that are currently (or in the future, see warning) validating the consumer chain. A _consumer validator_ is an opted-in +validator but not vice versa. For example, an opted-in validator `V` might not be a consumer validator because `V` is +denylisted or because `V` is removed due to a validator-set cap. + +:::warning +The returned consumer validators from this query do not necessarily correspond to the validator set that is +validating the consumer chain at this exact moment. This is because the `VSCPacket` sent to a consumer chain might be +delayed and hence this query might return the validator set that the consumer chain would have at some future +point in time. +::: + ### How can we see the commission rate a validator has set on a consumer chain? Using the following query: ```bash diff --git a/proto/interchain_security/ccv/provider/v1/query.proto b/proto/interchain_security/ccv/provider/v1/query.proto index d34bfd3e4c..b982cea667 100644 --- a/proto/interchain_security/ccv/provider/v1/query.proto +++ b/proto/interchain_security/ccv/provider/v1/query.proto @@ -134,6 +134,15 @@ service Query { option (google.api.http).get = "/interchain_security/ccv/provider/oldest_unconfirmed_vsc/{chain_id}"; } + + // QueryConsumerValidators returns the latest set consumer-validator set for a given chainID + // Note that this does not necessarily mean that the consumer chain is using this validator set at this exact moment + // because a VSCPacket could be delayed to be delivered on the consumer chain. + rpc QueryConsumerValidators(QueryConsumerValidatorsRequest) + returns (QueryConsumerValidatorsResponse) { + option (google.api.http).get = + "/interchain_security/ccv/provider/consumer_validators/{chain_id}"; + } } message QueryConsumerGenesisRequest { string chain_id = 1; } @@ -262,7 +271,6 @@ message QueryParamsResponse { Params params = 1 [(gogoproto.nullable) = false]; } - message QueryConsumerChainOptedInValidatorsRequest { string chain_id = 1; } @@ -272,6 +280,22 @@ message QueryConsumerChainOptedInValidatorsResponse { repeated string validators_provider_addresses = 1; } +message QueryConsumerValidatorsRequest { + string chain_id = 1; +} + +message QueryConsumerValidatorsValidator { + // The consensus address of the validator on the provider chain + string provider_address = 1 [ (gogoproto.moretags) = "yaml:\"address\"" ]; + // The consumer public key of the validator used on the consumer chain + tendermint.crypto.PublicKey consumer_key = 2; + // The power of the validator used on the consumer chain + int64 power = 3; +} + +message QueryConsumerValidatorsResponse { + repeated QueryConsumerValidatorsValidator validators = 1; +} message QueryConsumerChainsValidatorHasToValidateRequest { // The consensus address of the validator on the provider chain diff --git a/x/ccv/provider/client/cli/query.go b/x/ccv/provider/client/cli/query.go index f504a7557c..21d840f01f 100644 --- a/x/ccv/provider/client/cli/query.go +++ b/x/ccv/provider/client/cli/query.go @@ -36,6 +36,7 @@ func NewQueryCmd() *cobra.Command { cmd.AddCommand(CmdAllPairsValConAddrByConsumerChainID()) cmd.AddCommand(CmdProviderParameters()) cmd.AddCommand(CmdConsumerChainOptedInValidators()) + cmd.AddCommand(CmdConsumerValidators()) cmd.AddCommand(CmdConsumerChainsValidatorHasToValidate()) cmd.AddCommand(CmdValidatorConsumerCommissionRate()) cmd.AddCommand(CmdOldestUnconfirmedVsc()) @@ -448,6 +449,41 @@ $ %s consumer-opted-in-validators foochain return cmd } +// Command to query the consumer validators by consumer chain ID +func CmdConsumerValidators() *cobra.Command { + cmd := &cobra.Command{ + Use: "consumer-validators [chainid]", + Short: "Query the last set consumer-validator set for a given consumer chain", + Long: strings.TrimSpace( + fmt.Sprintf(`Query the last set consumer-validator set for a given consumer chain. +Note that this does not necessarily mean that the consumer chain is currently using this validator set because a VSCPacket could be delayed, etc. +Example: +$ %s consumer-validators foochain + `, version.AppName), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.QueryConsumerValidators(cmd.Context(), + &types.QueryConsumerValidatorsRequest{ChainId: args[0]}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + // Command to query the consumer chains list a given validator has to validate func CmdConsumerChainsValidatorHasToValidate() *cobra.Command { bech32PrefixConsAddr := sdk.GetConfig().GetBech32ConsensusAddrPrefix() diff --git a/x/ccv/provider/keeper/grpc_query.go b/x/ccv/provider/keeper/grpc_query.go index 84c5928fea..019c1dc957 100644 --- a/x/ccv/provider/keeper/grpc_query.go +++ b/x/ccv/provider/keeper/grpc_query.go @@ -252,6 +252,38 @@ func (k Keeper) QueryConsumerChainOptedInValidators(goCtx context.Context, req * }, nil } +// QueryConsumerValidators returns all validators that are consumer validators in a given consumer chain +func (k Keeper) QueryConsumerValidators(goCtx context.Context, req *types.QueryConsumerValidatorsRequest) (*types.QueryConsumerValidatorsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + consumerChainID := req.ChainId + if consumerChainID == "" { + return nil, status.Error(codes.InvalidArgument, "empty chainId") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + if _, found := k.GetConsumerClientId(ctx, consumerChainID); !found { + // chain has to have started; consumer client id is set for a chain during the chain's spawn time + return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("no started consumer chain: %s", consumerChainID)) + } + + var validators []*types.QueryConsumerValidatorsValidator + for _, v := range k.GetConsumerValSet(ctx, consumerChainID) { + validators = append(validators, &types.QueryConsumerValidatorsValidator{ + ProviderAddress: sdk.ConsAddress(v.ProviderConsAddr).String(), + ConsumerKey: v.ConsumerPublicKey, + Power: v.Power, + }) + } + + return &types.QueryConsumerValidatorsResponse{ + Validators: validators, + }, nil +} + // QueryConsumerChainsValidatorHasToValidate returns all consumer chains that the given validator has to validate now // or in the next epoch if nothing changes. func (k Keeper) QueryConsumerChainsValidatorHasToValidate(goCtx context.Context, req *types.QueryConsumerChainsValidatorHasToValidateRequest) (*types.QueryConsumerChainsValidatorHasToValidateResponse, error) { diff --git a/x/ccv/provider/keeper/grpc_query_test.go b/x/ccv/provider/keeper/grpc_query_test.go index 14e6e675e1..71a9b82b7e 100644 --- a/x/ccv/provider/keeper/grpc_query_test.go +++ b/x/ccv/provider/keeper/grpc_query_test.go @@ -1,6 +1,8 @@ package keeper_test import ( + "github.com/cometbft/cometbft/proto/tendermint/crypto" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "testing" "time" @@ -103,3 +105,147 @@ func TestQueryOldestUnconfirmedVsc(t *testing.T) { } require.Equal(t, expectedResult, response.VscSendTimestamp) } + +func TestQueryConsumerChainOptedInValidators(t *testing.T) { + chainID := "chainID" + + pk, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + req := types.QueryConsumerChainOptedInValidatorsRequest{ + ChainId: chainID, + } + + // error returned from not yet proposed or not yet registered chain + _, err := pk.QueryConsumerChainOptedInValidators(ctx, &req) + require.Error(t, err) + + pk.SetProposedConsumerChain(ctx, chainID, 1) + + providerAddr1 := types.NewProviderConsAddress([]byte("providerAddr1")) + providerAddr2 := types.NewProviderConsAddress([]byte("providerAddr2")) + expectedResponse := types.QueryConsumerChainOptedInValidatorsResponse{ + ValidatorsProviderAddresses: []string{providerAddr1.String(), providerAddr2.String()}, + } + + pk.SetOptedIn(ctx, chainID, providerAddr1) + pk.SetOptedIn(ctx, chainID, providerAddr2) + res, err := pk.QueryConsumerChainOptedInValidators(ctx, &req) + require.NoError(t, err) + require.Equal(t, &expectedResponse, res) +} + +func TestQueryConsumerValidators(t *testing.T) { + chainID := "chainID" + + pk, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + req := types.QueryConsumerValidatorsRequest{ + ChainId: chainID, + } + + // error returned from not-started chain + _, err := pk.QueryConsumerValidators(ctx, &req) + require.Error(t, err) + + providerAddr1 := types.NewProviderConsAddress([]byte("providerAddr1")) + consumerKey1 := cryptotestutil.NewCryptoIdentityFromIntSeed(1).TMProtoCryptoPublicKey() + consumerValidator1 := types.ConsumerValidator{ProviderConsAddr: providerAddr1.ToSdkConsAddr(), Power: 1, ConsumerPublicKey: &consumerKey1} + + providerAddr2 := types.NewProviderConsAddress([]byte("providerAddr2")) + consumerKey2 := cryptotestutil.NewCryptoIdentityFromIntSeed(2).TMProtoCryptoPublicKey() + consumerValidator2 := types.ConsumerValidator{ProviderConsAddr: providerAddr2.ToSdkConsAddr(), Power: 2, ConsumerPublicKey: &consumerKey2} + + expectedResponse := types.QueryConsumerValidatorsResponse{ + Validators: []*types.QueryConsumerValidatorsValidator{ + {providerAddr1.String(), &consumerKey1, 1}, + {providerAddr2.String(), &consumerKey2, 2}, + }, + } + + // set up the client id so the chain looks like it "started" + pk.SetConsumerClientId(ctx, chainID, "clientID") + pk.SetConsumerValSet(ctx, chainID, []types.ConsumerValidator{consumerValidator1, consumerValidator2}) + + res, err := pk.QueryConsumerValidators(ctx, &req) + require.NoError(t, err) + require.Equal(t, &expectedResponse, res) +} + +func TestQueryConsumerChainsValidatorHasToValidate(t *testing.T) { + pk, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + val := createStakingValidator(ctx, mocks, 1, 1) + valConsAddr, _ := val.GetConsAddr() + providerAddr := types.NewProviderConsAddress(valConsAddr) + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valConsAddr).Return(val, true).AnyTimes() + mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Return([]stakingtypes.Validator{val}).AnyTimes() + + req := types.QueryConsumerChainsValidatorHasToValidateRequest{ + ProviderAddress: providerAddr.String(), + } + + // set up some consumer chains + consumerChains := []string{"chain1", "chain2", "chain3", "chain4"} + for _, cc := range consumerChains { + pk.SetConsumerClientId(ctx, cc, "clientID") + } + + // set `providerAddr` as a consumer validator on "chain1" + pk.SetConsumerValidator(ctx, "chain1", types.ConsumerValidator{ + ProviderConsAddr: providerAddr.ToSdkConsAddr(), + Power: 1, + ConsumerPublicKey: &crypto.PublicKey{ + Sum: &crypto.PublicKey_Ed25519{ + Ed25519: []byte{1}, + }}}) + + // set `providerAddr` as an opted-in validator on "chain3" + pk.SetOptedIn(ctx, "chain3", providerAddr) + + // `providerAddr` has to validate "chain1" because it is a consumer validator in this chain, as well as "chain3" + // because it opted in, in "chain3" and `providerAddr` belongs to the bonded validators (see the mocking of `GetLastValidators` + // above) + expectedChains := []string{"chain1", "chain3"} + + res, err := pk.QueryConsumerChainsValidatorHasToValidate(ctx, &req) + require.NoError(t, err) + require.Equal(t, expectedChains, res.ConsumerChainIds) +} + +func TestQueryValidatorConsumerCommissionRate(t *testing.T) { + chainID := "chainID" + + pk, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + providerAddr := types.NewProviderConsAddress([]byte("providerAddr")) + req := types.QueryValidatorConsumerCommissionRateRequest{ + ChainId: chainID, + ProviderAddress: providerAddr.String(), + } + + // error returned from not yet proposed or not yet registered chain + _, err := pk.QueryValidatorConsumerCommissionRate(ctx, &req) + require.Error(t, err) + + pk.SetProposedConsumerChain(ctx, chainID, 1) + // validator with set consumer commission rate + expectedCommissionRate, _ := sdktypes.NewDecFromStr("0.123") + pk.SetConsumerCommissionRate(ctx, chainID, providerAddr, expectedCommissionRate) + res, _ := pk.QueryValidatorConsumerCommissionRate(ctx, &req) + require.Equal(t, expectedCommissionRate, res.Rate) + + // validator with no set consumer commission rate + pk.DeleteConsumerCommissionRate(ctx, chainID, providerAddr) + expectedCommissionRate, _ = sdktypes.NewDecFromStr("0.456") + + // because no consumer commission rate is set, the validator's set commission rate on the provider is used + val := stakingtypes.Validator{Commission: stakingtypes.Commission{CommissionRates: stakingtypes.CommissionRates{Rate: expectedCommissionRate}}} + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr( + ctx, providerAddr.ToSdkConsAddr()).Return(val, true).Times(1) + res, _ = pk.QueryValidatorConsumerCommissionRate(ctx, &req) + require.Equal(t, expectedCommissionRate, res.Rate) +} diff --git a/x/ccv/provider/types/query.pb.go b/x/ccv/provider/types/query.pb.go index b2a57c1c2f..4d42311fda 100644 --- a/x/ccv/provider/types/query.pb.go +++ b/x/ccv/provider/types/query.pb.go @@ -1309,6 +1309,157 @@ func (m *QueryConsumerChainOptedInValidatorsResponse) GetValidatorsProviderAddre return nil } +type QueryConsumerValidatorsRequest struct { + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` +} + +func (m *QueryConsumerValidatorsRequest) Reset() { *m = QueryConsumerValidatorsRequest{} } +func (m *QueryConsumerValidatorsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryConsumerValidatorsRequest) ProtoMessage() {} +func (*QueryConsumerValidatorsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_422512d7b7586cd7, []int{27} +} +func (m *QueryConsumerValidatorsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsumerValidatorsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsumerValidatorsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryConsumerValidatorsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsumerValidatorsRequest.Merge(m, src) +} +func (m *QueryConsumerValidatorsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryConsumerValidatorsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsumerValidatorsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsumerValidatorsRequest proto.InternalMessageInfo + +func (m *QueryConsumerValidatorsRequest) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +type QueryConsumerValidatorsValidator struct { + // The consensus address of the validator on the provider chain + ProviderAddress string `protobuf:"bytes,1,opt,name=provider_address,json=providerAddress,proto3" json:"provider_address,omitempty" yaml:"address"` + // The consumer public key of the validator used on the consumer chain + ConsumerKey *crypto.PublicKey `protobuf:"bytes,2,opt,name=consumer_key,json=consumerKey,proto3" json:"consumer_key,omitempty"` + // The power of the validator used on the consumer chain + Power int64 `protobuf:"varint,3,opt,name=power,proto3" json:"power,omitempty"` +} + +func (m *QueryConsumerValidatorsValidator) Reset() { *m = QueryConsumerValidatorsValidator{} } +func (m *QueryConsumerValidatorsValidator) String() string { return proto.CompactTextString(m) } +func (*QueryConsumerValidatorsValidator) ProtoMessage() {} +func (*QueryConsumerValidatorsValidator) Descriptor() ([]byte, []int) { + return fileDescriptor_422512d7b7586cd7, []int{28} +} +func (m *QueryConsumerValidatorsValidator) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsumerValidatorsValidator) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsumerValidatorsValidator.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryConsumerValidatorsValidator) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsumerValidatorsValidator.Merge(m, src) +} +func (m *QueryConsumerValidatorsValidator) XXX_Size() int { + return m.Size() +} +func (m *QueryConsumerValidatorsValidator) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsumerValidatorsValidator.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsumerValidatorsValidator proto.InternalMessageInfo + +func (m *QueryConsumerValidatorsValidator) GetProviderAddress() string { + if m != nil { + return m.ProviderAddress + } + return "" +} + +func (m *QueryConsumerValidatorsValidator) GetConsumerKey() *crypto.PublicKey { + if m != nil { + return m.ConsumerKey + } + return nil +} + +func (m *QueryConsumerValidatorsValidator) GetPower() int64 { + if m != nil { + return m.Power + } + return 0 +} + +type QueryConsumerValidatorsResponse struct { + Validators []*QueryConsumerValidatorsValidator `protobuf:"bytes,1,rep,name=validators,proto3" json:"validators,omitempty"` +} + +func (m *QueryConsumerValidatorsResponse) Reset() { *m = QueryConsumerValidatorsResponse{} } +func (m *QueryConsumerValidatorsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryConsumerValidatorsResponse) ProtoMessage() {} +func (*QueryConsumerValidatorsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_422512d7b7586cd7, []int{29} +} +func (m *QueryConsumerValidatorsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsumerValidatorsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsumerValidatorsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryConsumerValidatorsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsumerValidatorsResponse.Merge(m, src) +} +func (m *QueryConsumerValidatorsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryConsumerValidatorsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsumerValidatorsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsumerValidatorsResponse proto.InternalMessageInfo + +func (m *QueryConsumerValidatorsResponse) GetValidators() []*QueryConsumerValidatorsValidator { + if m != nil { + return m.Validators + } + return nil +} + type QueryConsumerChainsValidatorHasToValidateRequest struct { // The consensus address of the validator on the provider chain ProviderAddress string `protobuf:"bytes,1,opt,name=provider_address,json=providerAddress,proto3" json:"provider_address,omitempty" yaml:"address"` @@ -1322,7 +1473,7 @@ func (m *QueryConsumerChainsValidatorHasToValidateRequest) String() string { } func (*QueryConsumerChainsValidatorHasToValidateRequest) ProtoMessage() {} func (*QueryConsumerChainsValidatorHasToValidateRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_422512d7b7586cd7, []int{27} + return fileDescriptor_422512d7b7586cd7, []int{30} } func (m *QueryConsumerChainsValidatorHasToValidateRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1370,7 +1521,7 @@ func (m *QueryConsumerChainsValidatorHasToValidateResponse) String() string { } func (*QueryConsumerChainsValidatorHasToValidateResponse) ProtoMessage() {} func (*QueryConsumerChainsValidatorHasToValidateResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_422512d7b7586cd7, []int{28} + return fileDescriptor_422512d7b7586cd7, []int{31} } func (m *QueryConsumerChainsValidatorHasToValidateResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1420,7 +1571,7 @@ func (m *QueryValidatorConsumerCommissionRateRequest) String() string { } func (*QueryValidatorConsumerCommissionRateRequest) ProtoMessage() {} func (*QueryValidatorConsumerCommissionRateRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_422512d7b7586cd7, []int{29} + return fileDescriptor_422512d7b7586cd7, []int{32} } func (m *QueryValidatorConsumerCommissionRateRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1476,7 +1627,7 @@ func (m *QueryValidatorConsumerCommissionRateResponse) String() string { } func (*QueryValidatorConsumerCommissionRateResponse) ProtoMessage() {} func (*QueryValidatorConsumerCommissionRateResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_422512d7b7586cd7, []int{30} + return fileDescriptor_422512d7b7586cd7, []int{33} } func (m *QueryValidatorConsumerCommissionRateResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1513,7 +1664,7 @@ func (m *QueryOldestUnconfirmedVscRequest) Reset() { *m = QueryOldestUnc func (m *QueryOldestUnconfirmedVscRequest) String() string { return proto.CompactTextString(m) } func (*QueryOldestUnconfirmedVscRequest) ProtoMessage() {} func (*QueryOldestUnconfirmedVscRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_422512d7b7586cd7, []int{31} + return fileDescriptor_422512d7b7586cd7, []int{34} } func (m *QueryOldestUnconfirmedVscRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1557,7 +1708,7 @@ func (m *QueryOldestUnconfirmedVscResponse) Reset() { *m = QueryOldestUn func (m *QueryOldestUnconfirmedVscResponse) String() string { return proto.CompactTextString(m) } func (*QueryOldestUnconfirmedVscResponse) ProtoMessage() {} func (*QueryOldestUnconfirmedVscResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_422512d7b7586cd7, []int{32} + return fileDescriptor_422512d7b7586cd7, []int{35} } func (m *QueryOldestUnconfirmedVscResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1621,6 +1772,9 @@ func init() { proto.RegisterType((*QueryParamsResponse)(nil), "interchain_security.ccv.provider.v1.QueryParamsResponse") proto.RegisterType((*QueryConsumerChainOptedInValidatorsRequest)(nil), "interchain_security.ccv.provider.v1.QueryConsumerChainOptedInValidatorsRequest") proto.RegisterType((*QueryConsumerChainOptedInValidatorsResponse)(nil), "interchain_security.ccv.provider.v1.QueryConsumerChainOptedInValidatorsResponse") + proto.RegisterType((*QueryConsumerValidatorsRequest)(nil), "interchain_security.ccv.provider.v1.QueryConsumerValidatorsRequest") + proto.RegisterType((*QueryConsumerValidatorsValidator)(nil), "interchain_security.ccv.provider.v1.QueryConsumerValidatorsValidator") + proto.RegisterType((*QueryConsumerValidatorsResponse)(nil), "interchain_security.ccv.provider.v1.QueryConsumerValidatorsResponse") proto.RegisterType((*QueryConsumerChainsValidatorHasToValidateRequest)(nil), "interchain_security.ccv.provider.v1.QueryConsumerChainsValidatorHasToValidateRequest") proto.RegisterType((*QueryConsumerChainsValidatorHasToValidateResponse)(nil), "interchain_security.ccv.provider.v1.QueryConsumerChainsValidatorHasToValidateResponse") proto.RegisterType((*QueryValidatorConsumerCommissionRateRequest)(nil), "interchain_security.ccv.provider.v1.QueryValidatorConsumerCommissionRateRequest") @@ -1634,129 +1788,134 @@ func init() { } var fileDescriptor_422512d7b7586cd7 = []byte{ - // 1937 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcb, 0x6f, 0x1b, 0xc7, - 0x19, 0xd7, 0x52, 0xb2, 0x22, 0x8d, 0xe2, 0x47, 0xc6, 0x6e, 0x42, 0xaf, 0x64, 0x52, 0xd9, 0xb4, - 0x29, 0x2d, 0xdb, 0xbb, 0x12, 0xdd, 0xa0, 0x8e, 0x13, 0x45, 0x16, 0x45, 0xdb, 0x21, 0xec, 0xc4, - 0xcc, 0x5a, 0x56, 0x0b, 0xb7, 0xe8, 0x7a, 0xbd, 0x3b, 0xa1, 0x16, 0x5e, 0xee, 0xac, 0x76, 0x86, - 0x74, 0x08, 0x23, 0x40, 0xd3, 0x43, 0x9b, 0x53, 0x11, 0xf4, 0x01, 0xf4, 0x98, 0x4b, 0x8f, 0xbd, - 0x14, 0x45, 0xff, 0x85, 0xe6, 0xd6, 0xb4, 0xb9, 0x14, 0x3d, 0xb8, 0x85, 0xdc, 0x43, 0xd1, 0x53, - 0x11, 0x14, 0xe8, 0xa9, 0x40, 0xb1, 0xb3, 0xb3, 0x2f, 0x72, 0x45, 0x2e, 0x29, 0xe5, 0x24, 0xed, - 0xcc, 0x37, 0xbf, 0xef, 0x31, 0xdf, 0x63, 0x7e, 0x04, 0x8a, 0xe5, 0x50, 0xe4, 0x19, 0xbb, 0xba, - 0xe5, 0x68, 0x04, 0x19, 0x1d, 0xcf, 0xa2, 0x3d, 0xc5, 0x30, 0xba, 0x8a, 0xeb, 0xe1, 0xae, 0x65, - 0x22, 0x4f, 0xe9, 0xae, 0x29, 0x7b, 0x1d, 0xe4, 0xf5, 0x64, 0xd7, 0xc3, 0x14, 0xc3, 0x57, 0x32, - 0x0e, 0xc8, 0x86, 0xd1, 0x95, 0xc3, 0x03, 0x72, 0x77, 0x4d, 0x5c, 0x6a, 0x61, 0xdc, 0xb2, 0x91, - 0xa2, 0xbb, 0x96, 0xa2, 0x3b, 0x0e, 0xa6, 0x3a, 0xb5, 0xb0, 0x43, 0x02, 0x08, 0xf1, 0x4c, 0x0b, - 0xb7, 0x30, 0xfb, 0x57, 0xf1, 0xff, 0xe3, 0xab, 0x65, 0x7e, 0x86, 0x7d, 0x3d, 0xec, 0xbc, 0xaf, - 0x50, 0xab, 0x8d, 0x08, 0xd5, 0xdb, 0x2e, 0x17, 0xa8, 0xe6, 0x31, 0x35, 0xb2, 0x22, 0x38, 0xb3, - 0x7a, 0xd0, 0x99, 0xee, 0x9a, 0x42, 0x76, 0x75, 0x0f, 0x99, 0x9a, 0x81, 0x1d, 0xd2, 0x69, 0x47, - 0x27, 0xbe, 0x31, 0xe4, 0xc4, 0x63, 0xcb, 0x43, 0x5c, 0x6c, 0x89, 0x22, 0xc7, 0x44, 0x5e, 0xdb, - 0x72, 0xa8, 0x62, 0x78, 0x3d, 0x97, 0x62, 0xe5, 0x11, 0xea, 0x85, 0x1e, 0x9e, 0x35, 0x30, 0x69, - 0x63, 0xa2, 0x05, 0x4e, 0x06, 0x1f, 0xc1, 0x96, 0x74, 0x05, 0x2c, 0xbe, 0xe7, 0x87, 0x73, 0x8b, - 0xab, 0xbd, 0x89, 0x1c, 0x44, 0x2c, 0xa2, 0xa2, 0xbd, 0x0e, 0x22, 0x14, 0x9e, 0x05, 0x73, 0x81, - 0x6e, 0xcb, 0x2c, 0x0a, 0xcb, 0x42, 0x65, 0x5e, 0x7d, 0x8e, 0x7d, 0x37, 0x4c, 0xe9, 0x09, 0x58, - 0xca, 0x3e, 0x49, 0x5c, 0xec, 0x10, 0x04, 0xbf, 0x07, 0x8e, 0xb7, 0x82, 0x25, 0x8d, 0x50, 0x9d, - 0x22, 0x76, 0x7e, 0xa1, 0xba, 0x2a, 0x1f, 0x74, 0x63, 0xdd, 0x35, 0xb9, 0x0f, 0xeb, 0xae, 0x7f, - 0xae, 0x36, 0xf3, 0xd9, 0xd3, 0xf2, 0x94, 0xfa, 0x7c, 0x2b, 0xb1, 0x26, 0x2d, 0x01, 0x31, 0xa5, - 0x7c, 0xcb, 0x87, 0x0b, 0xad, 0x96, 0xf4, 0x3e, 0xa7, 0xc2, 0x5d, 0x6e, 0x59, 0x0d, 0xcc, 0x32, - 0xf5, 0xa4, 0x28, 0x2c, 0x4f, 0x57, 0x16, 0xaa, 0x2b, 0x72, 0x8e, 0x24, 0x92, 0x19, 0x88, 0xca, - 0x4f, 0x4a, 0xe7, 0xc1, 0x37, 0x07, 0x55, 0xdc, 0xa5, 0xba, 0x47, 0x9b, 0x1e, 0x76, 0x31, 0xd1, - 0xed, 0xc8, 0x9a, 0x8f, 0x05, 0x50, 0x19, 0x2d, 0xcb, 0x6d, 0xfb, 0x3e, 0x98, 0x77, 0xc3, 0x45, - 0x1e, 0xb1, 0xb7, 0xf2, 0x99, 0xc7, 0xc1, 0x37, 0x4d, 0xd3, 0xf2, 0xb3, 0x3b, 0x86, 0x8e, 0x01, - 0xa5, 0x0a, 0x78, 0x35, 0xcb, 0x12, 0xec, 0x0e, 0x18, 0xfd, 0x63, 0x21, 0xdb, 0xc1, 0x94, 0x68, - 0x74, 0xd3, 0x03, 0x36, 0xaf, 0x8f, 0x65, 0xb3, 0x8a, 0xda, 0xb8, 0xab, 0xdb, 0x99, 0x26, 0xff, - 0xaa, 0x00, 0x8e, 0x31, 0xdd, 0x43, 0x72, 0x11, 0x2e, 0x82, 0x79, 0xc3, 0xb6, 0x90, 0x43, 0xfd, - 0xbd, 0x02, 0xdb, 0x9b, 0x0b, 0x16, 0x1a, 0x26, 0x3c, 0x0d, 0x8e, 0x51, 0xec, 0x6a, 0xef, 0x16, - 0xa7, 0x97, 0x85, 0xca, 0x71, 0x75, 0x86, 0x62, 0xf7, 0x5d, 0xb8, 0x02, 0x60, 0xdb, 0x72, 0x34, - 0x17, 0x3f, 0x46, 0x9e, 0x66, 0x39, 0x5a, 0x20, 0x31, 0xb3, 0x2c, 0x54, 0xa6, 0xd5, 0x13, 0x6d, - 0xcb, 0x69, 0xfa, 0x1b, 0x0d, 0x67, 0xdb, 0x97, 0x5d, 0x05, 0x67, 0xba, 0xba, 0x6d, 0x99, 0x3a, - 0xc5, 0x1e, 0xe1, 0x47, 0x0c, 0xdd, 0x2d, 0x1e, 0x63, 0x78, 0x30, 0xde, 0x63, 0x87, 0xb6, 0x74, - 0x17, 0xae, 0x80, 0x17, 0xa2, 0x55, 0x8d, 0x20, 0xca, 0xc4, 0x67, 0x99, 0xf8, 0xc9, 0x68, 0xe3, - 0x2e, 0xa2, 0xbe, 0xec, 0x12, 0x98, 0xd7, 0x6d, 0x1b, 0x3f, 0xb6, 0x2d, 0x42, 0x8b, 0xcf, 0x2d, - 0x4f, 0x57, 0xe6, 0xd5, 0x78, 0x01, 0x8a, 0x60, 0xce, 0x44, 0x4e, 0x8f, 0x6d, 0xce, 0xb1, 0xcd, - 0xe8, 0x5b, 0xfa, 0x89, 0x00, 0x5e, 0x66, 0x77, 0xb4, 0x13, 0x42, 0x26, 0x92, 0xc0, 0x1b, 0x5d, - 0xc2, 0x70, 0x1d, 0x9c, 0x0a, 0xaf, 0x43, 0xd3, 0x4d, 0xd3, 0x43, 0x84, 0x04, 0xd1, 0xab, 0xc1, - 0x2f, 0x9f, 0x96, 0x4f, 0xf4, 0xf4, 0xb6, 0x7d, 0x55, 0xe2, 0x1b, 0x92, 0x7a, 0x32, 0x94, 0xdd, - 0x0c, 0x56, 0xae, 0xce, 0x7d, 0xfc, 0x69, 0x79, 0xea, 0x9f, 0x9f, 0x96, 0xa7, 0xa4, 0x3b, 0x40, - 0x1a, 0x66, 0x08, 0xcf, 0x93, 0xf3, 0xe0, 0x54, 0xd8, 0xdd, 0x22, 0x75, 0x81, 0x45, 0x27, 0x8d, - 0x84, 0xbc, 0xaf, 0x6c, 0xd0, 0xb5, 0x66, 0x42, 0x79, 0x3e, 0xd7, 0x06, 0x74, 0x0d, 0x71, 0xad, - 0x4f, 0xff, 0x30, 0xd7, 0xd2, 0x86, 0xc4, 0xae, 0x0d, 0x44, 0x92, 0xbb, 0xd6, 0x17, 0x35, 0x69, - 0x11, 0x9c, 0x65, 0x80, 0xdb, 0xbb, 0x1e, 0xa6, 0xd4, 0x46, 0xac, 0xa1, 0x85, 0x65, 0xf7, 0x27, - 0x81, 0x37, 0xb6, 0xbe, 0x5d, 0xae, 0xa6, 0x0c, 0x16, 0x88, 0xad, 0x93, 0x5d, 0xad, 0x8d, 0x28, - 0xf2, 0x98, 0x86, 0x69, 0x15, 0xb0, 0xa5, 0x77, 0xfc, 0x15, 0x58, 0x05, 0x5f, 0x4b, 0x08, 0x68, - 0x2c, 0x8f, 0x74, 0xc7, 0x40, 0xcc, 0xf7, 0x69, 0xf5, 0x74, 0x2c, 0xba, 0x19, 0x6e, 0xc1, 0x1f, - 0x80, 0xa2, 0x83, 0x3e, 0xa0, 0x9a, 0x87, 0x5c, 0x1b, 0x39, 0x16, 0xd9, 0xd5, 0x0c, 0xdd, 0x31, - 0x7d, 0x67, 0x11, 0x2b, 0x99, 0x85, 0xaa, 0x28, 0x07, 0xc3, 0x50, 0x0e, 0x87, 0xa1, 0xbc, 0x1d, - 0x0e, 0xc3, 0xda, 0x9c, 0xdf, 0x9d, 0x3f, 0xf9, 0x5b, 0x59, 0x50, 0x5f, 0xf4, 0x51, 0xd4, 0x10, - 0x64, 0x2b, 0xc4, 0x90, 0x2e, 0x82, 0x15, 0xe6, 0x92, 0x8a, 0x5a, 0x16, 0xa1, 0xc8, 0x43, 0x66, - 0x5c, 0xf7, 0x8f, 0x75, 0xcf, 0xac, 0x23, 0x07, 0xb7, 0xa3, 0xc6, 0x73, 0x1d, 0x5c, 0xc8, 0x25, - 0xcd, 0x23, 0xf2, 0x22, 0x98, 0x35, 0xd9, 0x0a, 0xeb, 0xe5, 0xf3, 0x2a, 0xff, 0x92, 0x4a, 0x7c, - 0x3a, 0x05, 0x3d, 0x05, 0x99, 0xac, 0x85, 0x34, 0xea, 0x91, 0x9a, 0x8f, 0x04, 0x70, 0xee, 0x00, - 0x01, 0x8e, 0xfc, 0x00, 0x9c, 0x70, 0x93, 0x7b, 0xe1, 0xb4, 0xa8, 0xe6, 0x6a, 0x6d, 0x29, 0x58, - 0x3e, 0xc2, 0xfa, 0xf0, 0xa4, 0x06, 0x38, 0x9e, 0x12, 0x83, 0x45, 0xc0, 0xf3, 0xb7, 0x9e, 0x4e, - 0xe7, 0x3a, 0x2c, 0x01, 0x10, 0xb6, 0xc4, 0x46, 0x9d, 0x5d, 0xe6, 0x8c, 0x9a, 0x58, 0x91, 0x6e, - 0x03, 0x85, 0x79, 0xb3, 0x69, 0xdb, 0x4d, 0xdd, 0xf2, 0xc8, 0x8e, 0x6e, 0x6f, 0x61, 0xc7, 0x4f, - 0xb9, 0x5a, 0xba, 0x83, 0x37, 0xea, 0x39, 0x46, 0xfb, 0xaf, 0x05, 0xb0, 0x9a, 0x1f, 0x8e, 0xc7, - 0x6b, 0x0f, 0xbc, 0xe0, 0xea, 0x96, 0xa7, 0x75, 0x75, 0xdb, 0x7f, 0xc4, 0xb0, 0x32, 0xe0, 0x21, - 0xbb, 0x91, 0x2f, 0x64, 0xba, 0xe5, 0xc5, 0x8a, 0xa2, 0x32, 0x73, 0xe2, 0x04, 0x38, 0xe1, 0xa6, - 0x44, 0xa4, 0xff, 0x08, 0xe0, 0xe5, 0x91, 0xa7, 0xe0, 0x8d, 0x83, 0x6a, 0xb3, 0xb6, 0xf8, 0xe5, - 0xd3, 0xf2, 0x4b, 0x41, 0x2b, 0xe8, 0x97, 0x18, 0x6c, 0x77, 0x3e, 0xce, 0x01, 0x2d, 0x25, 0x81, - 0xd3, 0x2f, 0x31, 0xd8, 0x5b, 0xe0, 0x06, 0x78, 0x3e, 0x92, 0x7a, 0x84, 0x7a, 0xbc, 0xc6, 0x96, - 0xe4, 0xf8, 0x09, 0x27, 0x07, 0x4f, 0x38, 0xb9, 0xd9, 0x79, 0x68, 0x5b, 0xc6, 0x2d, 0xd4, 0x53, - 0x17, 0xc2, 0x13, 0xb7, 0x50, 0x4f, 0x3a, 0x03, 0x60, 0x90, 0xba, 0xba, 0xa7, 0xc7, 0x85, 0xf3, - 0x00, 0x9c, 0x4e, 0xad, 0xf2, 0x6b, 0x69, 0x80, 0x59, 0x97, 0xad, 0xf0, 0xc9, 0x7c, 0x21, 0xe7, - 0x5d, 0xf8, 0x47, 0x78, 0xde, 0x72, 0x00, 0xe9, 0x26, 0x2f, 0xe4, 0x54, 0x06, 0xdc, 0x71, 0x29, - 0x32, 0x1b, 0x4e, 0xd4, 0x1e, 0xf3, 0x3c, 0x1d, 0xf7, 0x78, 0x8d, 0x8f, 0x02, 0x8a, 0xde, 0x6b, - 0xe7, 0x92, 0xf3, 0xb7, 0xef, 0xa6, 0x50, 0x58, 0xfa, 0x8b, 0x89, 0x41, 0x9c, 0xbe, 0x3a, 0x44, - 0xa4, 0x3d, 0x9e, 0xd1, 0xe9, 0x27, 0x61, 0xa4, 0xec, 0x6d, 0x9d, 0x6c, 0x63, 0xfe, 0x15, 0x36, - 0xe3, 0xcc, 0xf1, 0x28, 0xe4, 0x1e, 0x8f, 0x92, 0x0e, 0xd6, 0xc6, 0x50, 0xc9, 0x7d, 0xbd, 0x08, - 0x60, 0x94, 0x1c, 0x61, 0xf8, 0x42, 0x07, 0xa3, 0xf4, 0x0b, 0x4a, 0xcf, 0x64, 0x63, 0xf2, 0x42, - 0xf6, 0xe0, 0xdd, 0xc2, 0xed, 0xb6, 0x45, 0x88, 0x85, 0x1d, 0x35, 0xe1, 0xd1, 0x57, 0xf6, 0x16, - 0x90, 0x7e, 0x28, 0x80, 0x8b, 0xf9, 0x2c, 0xe1, 0x8e, 0x36, 0xc1, 0x8c, 0x17, 0xb2, 0x82, 0xf9, - 0xda, 0x9b, 0x7e, 0xa2, 0xfd, 0xf5, 0x69, 0xf9, 0xd5, 0x96, 0x45, 0x77, 0x3b, 0x0f, 0x65, 0x03, - 0xb7, 0x39, 0x4f, 0xe1, 0x7f, 0x2e, 0x11, 0xf3, 0x91, 0x42, 0x7b, 0x2e, 0x22, 0x72, 0x1d, 0x19, - 0x7f, 0xfe, 0xdd, 0x25, 0xc0, 0x69, 0x4c, 0x1d, 0x19, 0x2a, 0x43, 0x92, 0xd6, 0xc1, 0x32, 0xb3, - 0xe0, 0x8e, 0x6d, 0x22, 0x42, 0xef, 0x39, 0x06, 0x76, 0xde, 0xb7, 0xbc, 0x36, 0x32, 0x77, 0x88, - 0x91, 0x23, 0x29, 0x7f, 0x1a, 0x3e, 0x39, 0xb2, 0xcf, 0x73, 0xb3, 0x2d, 0x00, 0xbb, 0xc4, 0xd0, - 0x08, 0x72, 0x4c, 0x2d, 0x62, 0x84, 0xbc, 0xb4, 0x5e, 0xcb, 0x55, 0x5a, 0x3b, 0xc4, 0xb8, 0x8b, - 0x1c, 0x33, 0x9e, 0xa0, 0x41, 0x91, 0x9d, 0xea, 0xf6, 0xad, 0x57, 0xff, 0x70, 0x0e, 0x1c, 0x63, - 0x06, 0xc1, 0x7d, 0x01, 0x9c, 0xc9, 0xe2, 0x5a, 0xf0, 0x5a, 0x2e, 0x8d, 0x43, 0x08, 0x9e, 0xb8, - 0x79, 0x08, 0x84, 0x20, 0x24, 0xd2, 0xf5, 0x1f, 0x7d, 0xf1, 0x8f, 0x9f, 0x17, 0x36, 0xe0, 0xfa, - 0x68, 0xf2, 0x1e, 0xa5, 0x36, 0x27, 0x73, 0xca, 0x93, 0xf0, 0x36, 0x3e, 0x84, 0x5f, 0x08, 0xbc, - 0x81, 0xa5, 0xeb, 0x05, 0x6e, 0x8c, 0x6f, 0x61, 0x8a, 0x0d, 0x8a, 0xd7, 0x26, 0x07, 0xe0, 0x1e, - 0xbe, 0xce, 0x3c, 0xbc, 0x0c, 0xd7, 0xc6, 0xf0, 0x30, 0xe0, 0x89, 0xf0, 0xa3, 0x02, 0x28, 0x1e, - 0x40, 0xfe, 0x08, 0xbc, 0x3d, 0xa1, 0x65, 0x99, 0x3c, 0x53, 0x7c, 0xe7, 0x88, 0xd0, 0xb8, 0xd3, - 0x6f, 0x33, 0xa7, 0x6b, 0xf0, 0xda, 0xb8, 0x4e, 0xfb, 0x74, 0xdf, 0xa3, 0x5a, 0x44, 0xe1, 0xe0, - 0xff, 0x04, 0xf0, 0x52, 0x36, 0x97, 0x24, 0xf0, 0xd6, 0xc4, 0x46, 0x0f, 0x92, 0x56, 0xf1, 0xf6, - 0xd1, 0x80, 0xf1, 0x00, 0xdc, 0x64, 0x01, 0xd8, 0x84, 0x1b, 0x13, 0x04, 0x00, 0xbb, 0x09, 0xff, - 0xff, 0x1d, 0x3e, 0xea, 0x33, 0xe9, 0x11, 0xbc, 0x91, 0xdf, 0xea, 0x61, 0x44, 0x4f, 0xbc, 0x79, - 0x68, 0x1c, 0xee, 0xf8, 0x26, 0x73, 0xfc, 0x0d, 0xf8, 0x7a, 0x8e, 0x5f, 0xe3, 0x22, 0x96, 0x9b, - 0x7a, 0xf8, 0x64, 0xb8, 0x9c, 0x1c, 0xc9, 0x13, 0xb9, 0x9c, 0x41, 0x00, 0x27, 0x72, 0x39, 0x8b, - 0xbf, 0x4d, 0xe6, 0x72, 0x6a, 0x5e, 0xc2, 0x3f, 0x0a, 0xfc, 0x59, 0x96, 0xa2, 0x6e, 0xf0, 0xad, - 0xfc, 0x26, 0x66, 0x31, 0x42, 0x71, 0x63, 0xe2, 0xf3, 0xdc, 0xb5, 0x2b, 0xcc, 0xb5, 0x2a, 0x5c, - 0x1d, 0xed, 0x1a, 0xe5, 0x00, 0xc1, 0x0f, 0x76, 0xf0, 0x97, 0x05, 0xf0, 0x4a, 0x0e, 0x2e, 0x06, - 0xef, 0xe4, 0x37, 0x31, 0x17, 0x07, 0x14, 0x9b, 0x47, 0x07, 0xc8, 0x83, 0x70, 0x8b, 0x05, 0xe1, - 0x3a, 0xdc, 0x1a, 0x1d, 0x04, 0x2f, 0x42, 0x8c, 0x73, 0xda, 0x63, 0x98, 0x5a, 0xc0, 0x2d, 0xe1, - 0xbf, 0x06, 0xb8, 0x63, 0x9a, 0x12, 0x11, 0x38, 0xc6, 0x54, 0x3d, 0x80, 0xa0, 0x8a, 0xb5, 0xc3, - 0x40, 0x70, 0xaf, 0x6b, 0xcc, 0xeb, 0x37, 0xe1, 0xd5, 0xd1, 0x5e, 0x87, 0xd4, 0x54, 0xeb, 0x1f, - 0x60, 0xbf, 0x28, 0xf0, 0x5f, 0x2f, 0x73, 0x70, 0x41, 0xb8, 0x9d, 0xdf, 0xe8, 0xfc, 0x4c, 0x55, - 0xbc, 0x77, 0xc4, 0xa8, 0x3c, 0x3a, 0x6f, 0xb0, 0xe8, 0xbc, 0x06, 0x2f, 0x8f, 0xdd, 0xdf, 0x2d, - 0x13, 0xfe, 0x56, 0x00, 0x0b, 0x09, 0xba, 0x05, 0xbf, 0x3d, 0xc6, 0x75, 0x25, 0x69, 0x9b, 0x78, - 0x65, 0xfc, 0x83, 0xdc, 0xfe, 0x55, 0x66, 0xff, 0x0a, 0xac, 0xe4, 0xb8, 0xdd, 0xc0, 0xc8, 0x9f, - 0x85, 0x05, 0x3d, 0x9c, 0x78, 0x8d, 0x53, 0xd0, 0xb9, 0xb8, 0xe0, 0x38, 0x05, 0x9d, 0x8f, 0x13, - 0x8e, 0xf3, 0x3a, 0xc1, 0x3e, 0x88, 0x66, 0x39, 0x5a, 0xcc, 0x0f, 0x93, 0xef, 0xce, 0xdf, 0x17, - 0xc0, 0xf9, 0xdc, 0x3c, 0x0d, 0xde, 0x9b, 0xf4, 0x31, 0x39, 0x94, 0x6a, 0x8a, 0x3b, 0x47, 0x0d, - 0xcb, 0xc3, 0x74, 0x9f, 0x85, 0x69, 0x1b, 0xaa, 0x63, 0xbf, 0x5c, 0x35, 0x17, 0x79, 0x71, 0xc4, - 0x94, 0x27, 0xfd, 0xe4, 0xf0, 0x43, 0xf8, 0x9b, 0x02, 0xf8, 0x7a, 0x1e, 0xca, 0x07, 0x9b, 0x87, - 0x78, 0x98, 0x64, 0xf2, 0x58, 0xf1, 0xbd, 0x23, 0x44, 0xe4, 0x91, 0x7a, 0xc0, 0x22, 0x75, 0x1f, - 0x7e, 0x77, 0x9c, 0x48, 0x45, 0x50, 0x9a, 0xcf, 0x40, 0x13, 0x59, 0x95, 0x15, 0xaf, 0xff, 0x0a, - 0xfc, 0x97, 0xdf, 0x2c, 0x82, 0x09, 0xaf, 0xe7, 0x77, 0x69, 0x08, 0xc1, 0x15, 0x6f, 0x1c, 0x16, - 0x66, 0xfc, 0x81, 0x89, 0x19, 0x8e, 0xd6, 0x89, 0x81, 0xb4, 0x2e, 0x31, 0x12, 0xc1, 0xa8, 0x7d, - 0xe7, 0xb3, 0xfd, 0x92, 0xf0, 0xf9, 0x7e, 0x49, 0xf8, 0xfb, 0x7e, 0x49, 0xf8, 0xe4, 0x59, 0x69, - 0xea, 0xf3, 0x67, 0xa5, 0xa9, 0xbf, 0x3c, 0x2b, 0x4d, 0xdd, 0x5f, 0x1f, 0xe4, 0xfb, 0xb1, 0xbe, - 0x4b, 0x91, 0xbe, 0xee, 0xb7, 0x94, 0x0f, 0xfa, 0x9e, 0x2a, 0x3d, 0x17, 0x91, 0x87, 0xb3, 0xec, - 0x07, 0xe9, 0xcb, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x5f, 0xab, 0xc1, 0xc7, 0x37, 0x1e, 0x00, - 0x00, + // 2023 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcd, 0x6f, 0xdc, 0xc6, + 0x15, 0x17, 0x57, 0x1f, 0x91, 0x46, 0xf1, 0x47, 0xc6, 0x6a, 0x22, 0x53, 0xca, 0xae, 0xc2, 0xb4, + 0xa9, 0x2c, 0xdb, 0xa4, 0x24, 0x37, 0xa8, 0x63, 0x47, 0x91, 0xb5, 0x5a, 0xdb, 0x59, 0xd8, 0x89, + 0x15, 0x5a, 0x56, 0x0b, 0xb7, 0x28, 0x4d, 0x93, 0x13, 0x89, 0x30, 0x97, 0x43, 0x71, 0x66, 0xd7, + 0x59, 0x18, 0x01, 0x9a, 0x1e, 0xda, 0x9c, 0x8a, 0xa0, 0x1f, 0x40, 0x8f, 0xb9, 0xf4, 0xd8, 0x4b, + 0x51, 0x14, 0xf9, 0x13, 0x72, 0x6b, 0xda, 0x5c, 0x8a, 0x1e, 0xdc, 0xc2, 0xee, 0xa1, 0xe8, 0xa1, + 0x28, 0x8c, 0x02, 0x3d, 0x15, 0x28, 0x38, 0x1c, 0x7e, 0xed, 0x72, 0x77, 0xc9, 0x5d, 0xe5, 0xa4, + 0xe5, 0xcc, 0x9b, 0xdf, 0xbc, 0xf7, 0xe6, 0xbd, 0x37, 0xef, 0x37, 0x02, 0x8a, 0xe5, 0x50, 0xe4, + 0x19, 0x07, 0xba, 0xe5, 0x68, 0x04, 0x19, 0x4d, 0xcf, 0xa2, 0x6d, 0xc5, 0x30, 0x5a, 0x8a, 0xeb, + 0xe1, 0x96, 0x65, 0x22, 0x4f, 0x69, 0xad, 0x29, 0x87, 0x4d, 0xe4, 0xb5, 0x65, 0xd7, 0xc3, 0x14, + 0xc3, 0x57, 0x33, 0x16, 0xc8, 0x86, 0xd1, 0x92, 0xc3, 0x05, 0x72, 0x6b, 0x4d, 0x5c, 0xdc, 0xc7, + 0x78, 0xdf, 0x46, 0x8a, 0xee, 0x5a, 0x8a, 0xee, 0x38, 0x98, 0xea, 0xd4, 0xc2, 0x0e, 0x09, 0x20, + 0xc4, 0xb9, 0x7d, 0xbc, 0x8f, 0xd9, 0x4f, 0xc5, 0xff, 0xc5, 0x47, 0x2b, 0x7c, 0x0d, 0xfb, 0xba, + 0xdf, 0x7c, 0x5f, 0xa1, 0x56, 0x03, 0x11, 0xaa, 0x37, 0x5c, 0x2e, 0xb0, 0x9e, 0x47, 0xd5, 0x48, + 0x8b, 0x60, 0xcd, 0x6a, 0xaf, 0x35, 0xad, 0x35, 0x85, 0x1c, 0xe8, 0x1e, 0x32, 0x35, 0x03, 0x3b, + 0xa4, 0xd9, 0x88, 0x56, 0x7c, 0xa3, 0xcf, 0x8a, 0x87, 0x96, 0x87, 0xb8, 0xd8, 0x22, 0x45, 0x8e, + 0x89, 0xbc, 0x86, 0xe5, 0x50, 0xc5, 0xf0, 0xda, 0x2e, 0xc5, 0xca, 0x03, 0xd4, 0x0e, 0x2d, 0x3c, + 0x6d, 0x60, 0xd2, 0xc0, 0x44, 0x0b, 0x8c, 0x0c, 0x3e, 0x82, 0x29, 0xe9, 0x22, 0x58, 0x78, 0xcf, + 0x77, 0xe7, 0x36, 0xdf, 0xf6, 0x3a, 0x72, 0x10, 0xb1, 0x88, 0x8a, 0x0e, 0x9b, 0x88, 0x50, 0x78, + 0x1a, 0x4c, 0x07, 0x7b, 0x5b, 0xe6, 0xbc, 0xb0, 0x24, 0x2c, 0xcf, 0xa8, 0xcf, 0xb1, 0xef, 0xba, + 0x29, 0x3d, 0x02, 0x8b, 0xd9, 0x2b, 0x89, 0x8b, 0x1d, 0x82, 0xe0, 0xf7, 0xc0, 0xb1, 0xfd, 0x60, + 0x48, 0x23, 0x54, 0xa7, 0x88, 0xad, 0x9f, 0x5d, 0x5f, 0x95, 0x7b, 0x9d, 0x58, 0x6b, 0x4d, 0xee, + 0xc0, 0xba, 0xed, 0xaf, 0xab, 0x4e, 0x7c, 0xfe, 0xb8, 0x32, 0xa6, 0x3e, 0xbf, 0x9f, 0x18, 0x93, + 0x16, 0x81, 0x98, 0xda, 0x7c, 0xdb, 0x87, 0x0b, 0xb5, 0x96, 0xf4, 0x0e, 0xa3, 0xc2, 0x59, 0xae, + 0x59, 0x15, 0x4c, 0xb1, 0xed, 0xc9, 0xbc, 0xb0, 0x34, 0xbe, 0x3c, 0xbb, 0xbe, 0x22, 0xe7, 0x08, + 0x22, 0x99, 0x81, 0xa8, 0x7c, 0xa5, 0x74, 0x06, 0x7c, 0xb3, 0x7b, 0x8b, 0xdb, 0x54, 0xf7, 0xe8, + 0x8e, 0x87, 0x5d, 0x4c, 0x74, 0x3b, 0xd2, 0xe6, 0x63, 0x01, 0x2c, 0x0f, 0x96, 0xe5, 0xba, 0x7d, + 0x1f, 0xcc, 0xb8, 0xe1, 0x20, 0xf7, 0xd8, 0x5b, 0xf9, 0xd4, 0xe3, 0xe0, 0x5b, 0xa6, 0x69, 0xf9, + 0xd1, 0x1d, 0x43, 0xc7, 0x80, 0xd2, 0x32, 0x78, 0x2d, 0x4b, 0x13, 0xec, 0x76, 0x29, 0xfd, 0x63, + 0x21, 0xdb, 0xc0, 0x94, 0x68, 0x74, 0xd2, 0x5d, 0x3a, 0x6f, 0x14, 0xd2, 0x59, 0x45, 0x0d, 0xdc, + 0xd2, 0xed, 0x4c, 0x95, 0x7f, 0x55, 0x02, 0x93, 0x6c, 0xef, 0x3e, 0xb1, 0x08, 0x17, 0xc0, 0x8c, + 0x61, 0x5b, 0xc8, 0xa1, 0xfe, 0x5c, 0x89, 0xcd, 0x4d, 0x07, 0x03, 0x75, 0x13, 0x9e, 0x02, 0x93, + 0x14, 0xbb, 0xda, 0xbb, 0xf3, 0xe3, 0x4b, 0xc2, 0xf2, 0x31, 0x75, 0x82, 0x62, 0xf7, 0x5d, 0xb8, + 0x02, 0x60, 0xc3, 0x72, 0x34, 0x17, 0x3f, 0x44, 0x9e, 0x66, 0x39, 0x5a, 0x20, 0x31, 0xb1, 0x24, + 0x2c, 0x8f, 0xab, 0xc7, 0x1b, 0x96, 0xb3, 0xe3, 0x4f, 0xd4, 0x9d, 0x5d, 0x5f, 0x76, 0x15, 0xcc, + 0xb5, 0x74, 0xdb, 0x32, 0x75, 0x8a, 0x3d, 0xc2, 0x97, 0x18, 0xba, 0x3b, 0x3f, 0xc9, 0xf0, 0x60, + 0x3c, 0xc7, 0x16, 0x6d, 0xeb, 0x2e, 0x5c, 0x01, 0x2f, 0x44, 0xa3, 0x1a, 0x41, 0x94, 0x89, 0x4f, + 0x31, 0xf1, 0x13, 0xd1, 0xc4, 0x6d, 0x44, 0x7d, 0xd9, 0x45, 0x30, 0xa3, 0xdb, 0x36, 0x7e, 0x68, + 0x5b, 0x84, 0xce, 0x3f, 0xb7, 0x34, 0xbe, 0x3c, 0xa3, 0xc6, 0x03, 0x50, 0x04, 0xd3, 0x26, 0x72, + 0xda, 0x6c, 0x72, 0x9a, 0x4d, 0x46, 0xdf, 0xd2, 0x4f, 0x04, 0xf0, 0x0a, 0x3b, 0xa3, 0xbd, 0x10, + 0x32, 0x11, 0x04, 0xde, 0xe0, 0x14, 0x86, 0x1b, 0xe0, 0x64, 0x78, 0x1c, 0x9a, 0x6e, 0x9a, 0x1e, + 0x22, 0x24, 0xf0, 0x5e, 0x15, 0x3e, 0x7b, 0x5c, 0x39, 0xde, 0xd6, 0x1b, 0xf6, 0x25, 0x89, 0x4f, + 0x48, 0xea, 0x89, 0x50, 0x76, 0x2b, 0x18, 0xb9, 0x34, 0xfd, 0xf1, 0xa7, 0x95, 0xb1, 0x7f, 0x7c, + 0x5a, 0x19, 0x93, 0x6e, 0x01, 0xa9, 0x9f, 0x22, 0x3c, 0x4e, 0xce, 0x80, 0x93, 0x61, 0x75, 0x8b, + 0xb6, 0x0b, 0x34, 0x3a, 0x61, 0x24, 0xe4, 0xfd, 0xcd, 0xba, 0x4d, 0xdb, 0x49, 0x6c, 0x9e, 0xcf, + 0xb4, 0xae, 0xbd, 0xfa, 0x98, 0xd6, 0xb1, 0x7f, 0x3f, 0xd3, 0xd2, 0x8a, 0xc4, 0xa6, 0x75, 0x79, + 0x92, 0x9b, 0xd6, 0xe1, 0x35, 0x69, 0x01, 0x9c, 0x66, 0x80, 0xbb, 0x07, 0x1e, 0xa6, 0xd4, 0x46, + 0xac, 0xa0, 0x85, 0x69, 0xf7, 0x47, 0x81, 0x17, 0xb6, 0x8e, 0x59, 0xbe, 0x4d, 0x05, 0xcc, 0x12, + 0x5b, 0x27, 0x07, 0x5a, 0x03, 0x51, 0xe4, 0xb1, 0x1d, 0xc6, 0x55, 0xc0, 0x86, 0xde, 0xf1, 0x47, + 0xe0, 0x3a, 0xf8, 0x5a, 0x42, 0x40, 0x63, 0x71, 0xa4, 0x3b, 0x06, 0x62, 0xb6, 0x8f, 0xab, 0xa7, + 0x62, 0xd1, 0xad, 0x70, 0x0a, 0xfe, 0x00, 0xcc, 0x3b, 0xe8, 0x03, 0xaa, 0x79, 0xc8, 0xb5, 0x91, + 0x63, 0x91, 0x03, 0xcd, 0xd0, 0x1d, 0xd3, 0x37, 0x16, 0xb1, 0x94, 0x99, 0x5d, 0x17, 0xe5, 0xe0, + 0x32, 0x94, 0xc3, 0xcb, 0x50, 0xde, 0x0d, 0x2f, 0xc3, 0xea, 0xb4, 0x5f, 0x9d, 0x3f, 0xf9, 0x6b, + 0x45, 0x50, 0x5f, 0xf4, 0x51, 0xd4, 0x10, 0x64, 0x3b, 0xc4, 0x90, 0xce, 0x81, 0x15, 0x66, 0x92, + 0x8a, 0xf6, 0x2d, 0x42, 0x91, 0x87, 0xcc, 0x38, 0xef, 0x1f, 0xea, 0x9e, 0x59, 0x43, 0x0e, 0x6e, + 0x44, 0x85, 0xe7, 0x2a, 0x38, 0x9b, 0x4b, 0x9a, 0x7b, 0xe4, 0x45, 0x30, 0x65, 0xb2, 0x11, 0x56, + 0xcb, 0x67, 0x54, 0xfe, 0x25, 0x95, 0xf9, 0xed, 0x14, 0xd4, 0x14, 0x64, 0xb2, 0x12, 0x52, 0xaf, + 0x45, 0xdb, 0x7c, 0x24, 0x80, 0x97, 0x7b, 0x08, 0x70, 0xe4, 0x7b, 0xe0, 0xb8, 0x9b, 0x9c, 0x0b, + 0x6f, 0x8b, 0xf5, 0x5c, 0xa5, 0x2d, 0x05, 0xcb, 0xaf, 0xb0, 0x0e, 0x3c, 0xa9, 0x0e, 0x8e, 0xa5, + 0xc4, 0xe0, 0x3c, 0xe0, 0xf1, 0x5b, 0x4b, 0x87, 0x73, 0x0d, 0x96, 0x01, 0x08, 0x4b, 0x62, 0xbd, + 0xc6, 0x0e, 0x73, 0x42, 0x4d, 0x8c, 0x48, 0x37, 0x81, 0xc2, 0xac, 0xd9, 0xb2, 0xed, 0x1d, 0xdd, + 0xf2, 0xc8, 0x9e, 0x6e, 0x6f, 0x63, 0xc7, 0x0f, 0xb9, 0x6a, 0xba, 0x82, 0xd7, 0x6b, 0x39, 0xae, + 0xf6, 0x5f, 0x0b, 0x60, 0x35, 0x3f, 0x1c, 0xf7, 0xd7, 0x21, 0x78, 0xc1, 0xd5, 0x2d, 0x4f, 0x6b, + 0xe9, 0xb6, 0xdf, 0xc4, 0xb0, 0x34, 0xe0, 0x2e, 0xbb, 0x96, 0xcf, 0x65, 0xba, 0xe5, 0xc5, 0x1b, + 0x45, 0x69, 0xe6, 0xc4, 0x01, 0x70, 0xdc, 0x4d, 0x89, 0x48, 0xff, 0x11, 0xc0, 0x2b, 0x03, 0x57, + 0xc1, 0x6b, 0xbd, 0x72, 0xb3, 0xba, 0xf0, 0xec, 0x71, 0xe5, 0xa5, 0xa0, 0x14, 0x74, 0x4a, 0x74, + 0x97, 0x3b, 0x1f, 0xa7, 0x47, 0x49, 0x49, 0xe0, 0x74, 0x4a, 0x74, 0xd7, 0x16, 0xb8, 0x09, 0x9e, + 0x8f, 0xa4, 0x1e, 0xa0, 0x36, 0xcf, 0xb1, 0x45, 0x39, 0x6e, 0xe1, 0xe4, 0xa0, 0x85, 0x93, 0x77, + 0x9a, 0xf7, 0x6d, 0xcb, 0xb8, 0x81, 0xda, 0xea, 0x6c, 0xb8, 0xe2, 0x06, 0x6a, 0x4b, 0x73, 0x00, + 0x06, 0xa1, 0xab, 0x7b, 0x7a, 0x9c, 0x38, 0xf7, 0xc0, 0xa9, 0xd4, 0x28, 0x3f, 0x96, 0x3a, 0x98, + 0x72, 0xd9, 0x08, 0xbf, 0x99, 0xcf, 0xe6, 0x3c, 0x0b, 0x7f, 0x09, 0x8f, 0x5b, 0x0e, 0x20, 0x5d, + 0xe7, 0x89, 0x9c, 0x8a, 0x80, 0x5b, 0x2e, 0x45, 0x66, 0xdd, 0x89, 0xca, 0x63, 0x9e, 0xd6, 0xf1, + 0x90, 0xe7, 0xf8, 0x20, 0xa0, 0xa8, 0x5f, 0x7b, 0x39, 0x79, 0xff, 0x76, 0x9c, 0x14, 0x0a, 0x53, + 0x7f, 0x21, 0x71, 0x11, 0xa7, 0x8f, 0x0e, 0x11, 0xe9, 0x32, 0x28, 0xa7, 0xb6, 0x2c, 0xa4, 0xef, + 0x67, 0x02, 0x58, 0xea, 0xb1, 0x3a, 0xfa, 0x95, 0x79, 0x99, 0x0a, 0xb9, 0x2f, 0xd3, 0xae, 0xa8, + 0x28, 0x15, 0x8c, 0x0a, 0x38, 0x07, 0x26, 0x59, 0x6b, 0xc2, 0xe2, 0x69, 0x5c, 0x0d, 0x3e, 0xfc, + 0xe6, 0xb3, 0xd2, 0xd3, 0x70, 0xee, 0x5f, 0x04, 0x40, 0xec, 0x3a, 0x9e, 0xb2, 0x57, 0x73, 0x85, + 0xc9, 0x20, 0xa7, 0xa8, 0x09, 0x60, 0xe9, 0x90, 0x17, 0x95, 0x74, 0x57, 0x1e, 0xc9, 0xbe, 0xad, + 0x93, 0x5d, 0xcc, 0xbf, 0xc2, 0xfb, 0x70, 0x44, 0xa7, 0x4a, 0x3a, 0x58, 0x2b, 0xb0, 0x25, 0x77, + 0xc7, 0x39, 0x00, 0xa3, 0x93, 0x08, 0x23, 0x22, 0x8c, 0xb1, 0xa8, 0x02, 0x04, 0xd5, 0xcf, 0x64, + 0x9d, 0xca, 0xd9, 0xec, 0xde, 0x67, 0x1b, 0x37, 0x1a, 0x16, 0x21, 0x16, 0x76, 0xd4, 0x84, 0x45, + 0x5f, 0x59, 0x3b, 0x26, 0xfd, 0x50, 0x00, 0xe7, 0xf2, 0x69, 0xc2, 0x0d, 0xdd, 0x01, 0x13, 0x5e, + 0x48, 0xcc, 0x66, 0xaa, 0x6f, 0xfa, 0xb9, 0xfe, 0x97, 0xc7, 0x95, 0xd7, 0xf6, 0x2d, 0x7a, 0xd0, + 0xbc, 0x2f, 0x1b, 0xb8, 0xc1, 0xa9, 0x22, 0xff, 0x73, 0x9e, 0x98, 0x0f, 0x14, 0xda, 0x76, 0x11, + 0x91, 0x6b, 0xc8, 0xf8, 0xd3, 0xef, 0xce, 0x03, 0xce, 0x24, 0x6b, 0xc8, 0x50, 0x19, 0x92, 0xb4, + 0xc1, 0xf3, 0xe4, 0x96, 0x6d, 0x22, 0x42, 0xef, 0x38, 0x06, 0x76, 0xde, 0xb7, 0xbc, 0x06, 0x32, + 0xf7, 0x88, 0x91, 0x23, 0xcf, 0x7e, 0x1a, 0x76, 0x7d, 0xd9, 0xeb, 0xb9, 0xda, 0x16, 0x80, 0x2d, + 0x62, 0x68, 0x04, 0x39, 0xa6, 0x16, 0x91, 0x72, 0x5e, 0xdd, 0x5e, 0xcf, 0x15, 0xb6, 0x7b, 0xc4, + 0xb8, 0x8d, 0x1c, 0x33, 0x6e, 0x62, 0x82, 0x3a, 0x77, 0xb2, 0xd5, 0x31, 0xbe, 0xfe, 0x59, 0x05, + 0x4c, 0x32, 0x85, 0xe0, 0x13, 0x01, 0xcc, 0x65, 0xd1, 0x5d, 0x78, 0xa5, 0x78, 0xa2, 0xa4, 0x39, + 0xb6, 0xb8, 0x35, 0x02, 0x42, 0xe0, 0x12, 0xe9, 0xea, 0x8f, 0xbe, 0xfc, 0xfb, 0xcf, 0x4b, 0x9b, + 0x70, 0x63, 0xf0, 0xfb, 0x49, 0x14, 0xda, 0x9c, 0x4f, 0x2b, 0x8f, 0xc2, 0xd3, 0xf8, 0x10, 0x7e, + 0x29, 0xf0, 0x3b, 0x24, 0x9d, 0x2f, 0x70, 0xb3, 0xb8, 0x86, 0x29, 0x42, 0x2e, 0x5e, 0x19, 0x1e, + 0x80, 0x5b, 0xf8, 0x06, 0xb3, 0xf0, 0x02, 0x5c, 0x2b, 0x60, 0x61, 0x40, 0xd5, 0xe1, 0x47, 0x25, + 0x30, 0xdf, 0x83, 0x7f, 0x13, 0x78, 0x73, 0x48, 0xcd, 0x32, 0xa9, 0xbe, 0xf8, 0xce, 0x11, 0xa1, + 0x71, 0xa3, 0xdf, 0x66, 0x46, 0x57, 0xe1, 0x95, 0xa2, 0x46, 0x6b, 0xc4, 0x07, 0xd4, 0x22, 0x16, + 0x0d, 0xff, 0x27, 0x80, 0x97, 0xb2, 0xe9, 0x3c, 0x81, 0x37, 0x86, 0x56, 0xba, 0xfb, 0xdd, 0x40, + 0xbc, 0x79, 0x34, 0x60, 0xdc, 0x01, 0xd7, 0x99, 0x03, 0xb6, 0xe0, 0xe6, 0x10, 0x0e, 0xc0, 0x6e, + 0xc2, 0xfe, 0x7f, 0x87, 0xbc, 0x2a, 0x93, 0xa1, 0xc2, 0x6b, 0xf9, 0xb5, 0xee, 0xc7, 0xb5, 0xc5, + 0xeb, 0x23, 0xe3, 0x70, 0xc3, 0xb7, 0x98, 0xe1, 0x97, 0xe1, 0x1b, 0x39, 0x1e, 0x44, 0xa3, 0x87, + 0x86, 0x54, 0xef, 0x99, 0x61, 0x72, 0xb2, 0x2b, 0x1a, 0xca, 0xe4, 0x0c, 0x0e, 0x3e, 0x94, 0xc9, + 0x59, 0x14, 0x7a, 0x38, 0x93, 0x53, 0xf7, 0x25, 0xfc, 0x83, 0xc0, 0x3b, 0xe3, 0x14, 0x7b, 0x86, + 0x6f, 0xe5, 0x57, 0x31, 0x8b, 0x94, 0x8b, 0x9b, 0x43, 0xaf, 0xe7, 0xa6, 0x5d, 0x64, 0xa6, 0xad, + 0xc3, 0xd5, 0xc1, 0xa6, 0x51, 0x0e, 0x10, 0xbc, 0x99, 0xc2, 0x5f, 0x96, 0xc0, 0xab, 0x39, 0xe8, + 0x30, 0xbc, 0x95, 0x5f, 0xc5, 0x5c, 0x34, 0x5c, 0xdc, 0x39, 0x3a, 0x40, 0xee, 0x84, 0x1b, 0xcc, + 0x09, 0x57, 0xe1, 0xf6, 0x60, 0x27, 0x78, 0x11, 0x62, 0x1c, 0xd3, 0x1e, 0xc3, 0xd4, 0x02, 0x7a, + 0x0f, 0xff, 0xd9, 0x45, 0xdf, 0xd3, 0xac, 0x94, 0xc0, 0x02, 0xb7, 0x6a, 0x8f, 0x37, 0x02, 0xb1, + 0x3a, 0x0a, 0x04, 0xb7, 0xba, 0xca, 0xac, 0x7e, 0x13, 0x5e, 0x1a, 0x6c, 0x75, 0xf8, 0x3a, 0xa0, + 0x75, 0x5e, 0x60, 0xbf, 0x28, 0xf1, 0x07, 0xe4, 0x1c, 0x74, 0x1c, 0xee, 0xe6, 0x57, 0x3a, 0xff, + 0x63, 0x81, 0x78, 0xe7, 0x88, 0x51, 0xb9, 0x77, 0x2e, 0x33, 0xef, 0xbc, 0x0e, 0x2f, 0x14, 0xae, + 0xef, 0x96, 0x09, 0x7f, 0x2b, 0x80, 0xd9, 0x04, 0xe3, 0x85, 0xdf, 0x2e, 0x70, 0x5c, 0x49, 0xe6, + 0x2c, 0x5e, 0x2c, 0xbe, 0x90, 0xeb, 0xbf, 0xca, 0xf4, 0x5f, 0x81, 0xcb, 0x39, 0x4e, 0x37, 0x50, + 0xf2, 0x67, 0x61, 0x42, 0xf7, 0xe7, 0xbe, 0x45, 0x12, 0x3a, 0x17, 0x1d, 0x2f, 0x92, 0xd0, 0xf9, + 0x68, 0x79, 0x91, 0xee, 0x04, 0xfb, 0x20, 0x9a, 0xe5, 0x68, 0x31, 0x1d, 0x4c, 0xf6, 0x9d, 0xbf, + 0x2f, 0x81, 0x33, 0xb9, 0x79, 0x1a, 0xbc, 0x33, 0x6c, 0x33, 0xd9, 0x97, 0x6a, 0x8a, 0x7b, 0x47, + 0x0d, 0xcb, 0xdd, 0x74, 0x97, 0xb9, 0x69, 0x17, 0xaa, 0x85, 0x3b, 0x57, 0xcd, 0x45, 0x5e, 0xec, + 0x31, 0xe5, 0x51, 0x27, 0x39, 0xfc, 0x10, 0xfe, 0xa6, 0x04, 0xbe, 0x9e, 0x87, 0xf2, 0xc1, 0x9d, + 0x11, 0x1a, 0x93, 0x4c, 0x1e, 0x2b, 0xbe, 0x77, 0x84, 0x88, 0xdc, 0x53, 0xf7, 0x98, 0xa7, 0xee, + 0xc2, 0xef, 0x16, 0xf1, 0x54, 0x04, 0xa5, 0xf9, 0x0c, 0x34, 0x11, 0x55, 0x59, 0xfe, 0xfa, 0xaf, + 0xc0, 0x1f, 0xdf, 0xb3, 0x08, 0x26, 0x2c, 0xf0, 0xe6, 0xd1, 0x87, 0xe0, 0x8a, 0xd7, 0x46, 0x85, + 0x29, 0x7e, 0x61, 0x62, 0x86, 0xa3, 0x35, 0x63, 0x20, 0xad, 0x45, 0x8c, 0x64, 0x8a, 0xfd, 0xab, + 0x93, 0x00, 0x24, 0x6a, 0xcd, 0xf6, 0x28, 0x6f, 0x3d, 0xa1, 0xd5, 0xb5, 0xd1, 0x40, 0x46, 0x60, + 0x3c, 0x99, 0x35, 0xa5, 0xfa, 0x9d, 0xcf, 0x9f, 0x94, 0x85, 0x2f, 0x9e, 0x94, 0x85, 0xbf, 0x3d, + 0x29, 0x0b, 0x9f, 0x3c, 0x2d, 0x8f, 0x7d, 0xf1, 0xb4, 0x3c, 0xf6, 0xe7, 0xa7, 0xe5, 0xb1, 0xbb, + 0x1b, 0xdd, 0x0f, 0x1c, 0xf1, 0x66, 0xe7, 0xa3, 0xcd, 0x5a, 0xdf, 0x52, 0x3e, 0xe8, 0xe8, 0xcd, + 0xda, 0x2e, 0x22, 0xf7, 0xa7, 0xd8, 0x3f, 0x41, 0x2e, 0xfc, 0x3f, 0x00, 0x00, 0xff, 0xff, 0xb7, + 0x19, 0x6f, 0x26, 0xab, 0x20, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1812,6 +1971,10 @@ type QueryClient interface { QueryValidatorConsumerCommissionRate(ctx context.Context, in *QueryValidatorConsumerCommissionRateRequest, opts ...grpc.CallOption) (*QueryValidatorConsumerCommissionRateResponse, error) // QueryOldestUnconfirmedVsc returns the send timestamp of the oldest unconfirmed VSCPacket for a given chainID QueryOldestUnconfirmedVsc(ctx context.Context, in *QueryOldestUnconfirmedVscRequest, opts ...grpc.CallOption) (*QueryOldestUnconfirmedVscResponse, error) + // QueryConsumerValidators returns the latest set consumer-validator set for a given chainID + // Note that this does not necessarily mean that the consumer chain is using this validator set at this exact moment + // because a VSCPacket could be delayed to be delivered on the consumer chain. + QueryConsumerValidators(ctx context.Context, in *QueryConsumerValidatorsRequest, opts ...grpc.CallOption) (*QueryConsumerValidatorsResponse, error) } type queryClient struct { @@ -1957,6 +2120,15 @@ func (c *queryClient) QueryOldestUnconfirmedVsc(ctx context.Context, in *QueryOl return out, nil } +func (c *queryClient) QueryConsumerValidators(ctx context.Context, in *QueryConsumerValidatorsRequest, opts ...grpc.CallOption) (*QueryConsumerValidatorsResponse, error) { + out := new(QueryConsumerValidatorsResponse) + err := c.cc.Invoke(ctx, "/interchain_security.ccv.provider.v1.Query/QueryConsumerValidators", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // ConsumerGenesis queries the genesis state needed to start a consumer chain @@ -2000,6 +2172,10 @@ type QueryServer interface { QueryValidatorConsumerCommissionRate(context.Context, *QueryValidatorConsumerCommissionRateRequest) (*QueryValidatorConsumerCommissionRateResponse, error) // QueryOldestUnconfirmedVsc returns the send timestamp of the oldest unconfirmed VSCPacket for a given chainID QueryOldestUnconfirmedVsc(context.Context, *QueryOldestUnconfirmedVscRequest) (*QueryOldestUnconfirmedVscResponse, error) + // QueryConsumerValidators returns the latest set consumer-validator set for a given chainID + // Note that this does not necessarily mean that the consumer chain is using this validator set at this exact moment + // because a VSCPacket could be delayed to be delivered on the consumer chain. + QueryConsumerValidators(context.Context, *QueryConsumerValidatorsRequest) (*QueryConsumerValidatorsResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -2051,6 +2227,9 @@ func (*UnimplementedQueryServer) QueryValidatorConsumerCommissionRate(ctx contex func (*UnimplementedQueryServer) QueryOldestUnconfirmedVsc(ctx context.Context, req *QueryOldestUnconfirmedVscRequest) (*QueryOldestUnconfirmedVscResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryOldestUnconfirmedVsc not implemented") } +func (*UnimplementedQueryServer) QueryConsumerValidators(ctx context.Context, req *QueryConsumerValidatorsRequest) (*QueryConsumerValidatorsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryConsumerValidators not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -2326,6 +2505,24 @@ func _Query_QueryOldestUnconfirmedVsc_Handler(srv interface{}, ctx context.Conte return interceptor(ctx, in, info, handler) } +func _Query_QueryConsumerValidators_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryConsumerValidatorsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryConsumerValidators(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/interchain_security.ccv.provider.v1.Query/QueryConsumerValidators", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryConsumerValidators(ctx, req.(*QueryConsumerValidatorsRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "interchain_security.ccv.provider.v1.Query", HandlerType: (*QueryServer)(nil), @@ -2390,6 +2587,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "QueryOldestUnconfirmedVsc", Handler: _Query_QueryOldestUnconfirmedVsc_Handler, }, + { + MethodName: "QueryConsumerValidators", + Handler: _Query_QueryConsumerValidators_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "interchain_security/ccv/provider/v1/query.proto", @@ -3291,6 +3492,120 @@ func (m *QueryConsumerChainOptedInValidatorsResponse) MarshalToSizedBuffer(dAtA return len(dAtA) - i, nil } +func (m *QueryConsumerValidatorsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryConsumerValidatorsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsumerValidatorsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryConsumerValidatorsValidator) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryConsumerValidatorsValidator) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsumerValidatorsValidator) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Power != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Power)) + i-- + dAtA[i] = 0x18 + } + if m.ConsumerKey != nil { + { + size, err := m.ConsumerKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.ProviderAddress) > 0 { + i -= len(m.ProviderAddress) + copy(dAtA[i:], m.ProviderAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ProviderAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryConsumerValidatorsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryConsumerValidatorsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsumerValidatorsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Validators) > 0 { + for iNdEx := len(m.Validators) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Validators[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func (m *QueryConsumerChainsValidatorHasToValidateRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -3877,7 +4192,20 @@ func (m *QueryConsumerChainOptedInValidatorsResponse) Size() (n int) { return n } -func (m *QueryConsumerChainsValidatorHasToValidateRequest) Size() (n int) { +func (m *QueryConsumerValidatorsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryConsumerValidatorsValidator) Size() (n int) { if m == nil { return 0 } @@ -3887,35 +4215,70 @@ func (m *QueryConsumerChainsValidatorHasToValidateRequest) Size() (n int) { if l > 0 { n += 1 + l + sovQuery(uint64(l)) } + if m.ConsumerKey != nil { + l = m.ConsumerKey.Size() + n += 1 + l + sovQuery(uint64(l)) + } + if m.Power != 0 { + n += 1 + sovQuery(uint64(m.Power)) + } return n } -func (m *QueryConsumerChainsValidatorHasToValidateResponse) Size() (n int) { +func (m *QueryConsumerValidatorsResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l - if len(m.ConsumerChainIds) > 0 { - for _, s := range m.ConsumerChainIds { - l = len(s) + if len(m.Validators) > 0 { + for _, e := range m.Validators { + l = e.Size() n += 1 + l + sovQuery(uint64(l)) } } return n } -func (m *QueryValidatorConsumerCommissionRateRequest) Size() (n int) { +func (m *QueryConsumerChainsValidatorHasToValidateRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l - l = len(m.ChainId) + l = len(m.ProviderAddress) if l > 0 { n += 1 + l + sovQuery(uint64(l)) } - l = len(m.ProviderAddress) + return n +} + +func (m *QueryConsumerChainsValidatorHasToValidateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ConsumerChainIds) > 0 { + for _, s := range m.ConsumerChainIds { + l = len(s) + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryValidatorConsumerCommissionRateRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ProviderAddress) if l > 0 { n += 1 + l + sovQuery(uint64(l)) } @@ -6331,6 +6694,309 @@ func (m *QueryConsumerChainOptedInValidatorsResponse) Unmarshal(dAtA []byte) err } return nil } +func (m *QueryConsumerValidatorsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryConsumerValidatorsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsumerValidatorsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryConsumerValidatorsValidator) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryConsumerValidatorsValidator: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsumerValidatorsValidator: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProviderAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProviderAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsumerKey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ConsumerKey == nil { + m.ConsumerKey = &crypto.PublicKey{} + } + if err := m.ConsumerKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Power", wireType) + } + m.Power = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Power |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryConsumerValidatorsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryConsumerValidatorsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsumerValidatorsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Validators", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Validators = append(m.Validators, &QueryConsumerValidatorsValidator{}) + if err := m.Validators[len(m.Validators)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *QueryConsumerChainsValidatorHasToValidateRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/ccv/provider/types/query.pb.gw.go b/x/ccv/provider/types/query.pb.gw.go index 2be0974fbf..16bb010d71 100644 --- a/x/ccv/provider/types/query.pb.gw.go +++ b/x/ccv/provider/types/query.pb.gw.go @@ -559,6 +559,60 @@ func local_request_Query_QueryOldestUnconfirmedVsc_0(ctx context.Context, marsha } +func request_Query_QueryConsumerValidators_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConsumerValidatorsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["chain_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "chain_id") + } + + protoReq.ChainId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chain_id", err) + } + + msg, err := client.QueryConsumerValidators(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryConsumerValidators_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConsumerValidatorsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["chain_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "chain_id") + } + + protoReq.ChainId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chain_id", err) + } + + msg, err := server.QueryConsumerValidators(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -910,6 +964,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_QueryConsumerValidators_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryConsumerValidators_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryConsumerValidators_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1251,6 +1328,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_QueryConsumerValidators_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryConsumerValidators_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryConsumerValidators_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1284,6 +1381,8 @@ var ( pattern_Query_QueryValidatorConsumerCommissionRate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"interchain_security", "ccv", "provider", "consumer_commission_rate", "chain_id", "provider_address"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_QueryOldestUnconfirmedVsc_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"interchain_security", "ccv", "provider", "oldest_unconfirmed_vsc", "chain_id"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryConsumerValidators_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"interchain_security", "ccv", "provider", "consumer_validators", "chain_id"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( @@ -1316,4 +1415,6 @@ var ( forward_Query_QueryValidatorConsumerCommissionRate_0 = runtime.ForwardResponseMessage forward_Query_QueryOldestUnconfirmedVsc_0 = runtime.ForwardResponseMessage + + forward_Query_QueryConsumerValidators_0 = runtime.ForwardResponseMessage ) From a0645d80636cb60dabf285bc21375d927f4295e4 Mon Sep 17 00:00:00 2001 From: insumity Date: Fri, 17 May 2024 11:22:20 +0200 Subject: [PATCH 029/102] fix!: drop chain proposals with empty validator set at spawn time (#1888) * init commit * Update x/ccv/provider/keeper/proposal.go Co-authored-by: MSalopek * added one more test case --------- Co-authored-by: MSalopek --- testutil/keeper/expectations.go | 2 - testutil/keeper/unit_test_helpers.go | 1 + x/ccv/provider/keeper/proposal.go | 13 +++++++ x/ccv/provider/keeper/proposal_test.go | 49 ++++++++++++++++++++++++- x/ccv/provider/proposal_handler_test.go | 1 + 5 files changed, 63 insertions(+), 3 deletions(-) diff --git a/testutil/keeper/expectations.go b/testutil/keeper/expectations.go index 34446bd0c3..8a010ce405 100644 --- a/testutil/keeper/expectations.go +++ b/testutil/keeper/expectations.go @@ -57,8 +57,6 @@ func GetMocksForMakeConsumerGenesis(ctx sdk.Context, mocks *MockedKeepers, mocks.MockClientKeeper.EXPECT().GetSelfConsensusState(gomock.Any(), clienttypes.GetSelfHeight(ctx)).Return(&ibctmtypes.ConsensusState{}, nil).Times(1), - - mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Times(1), } } diff --git a/testutil/keeper/unit_test_helpers.go b/testutil/keeper/unit_test_helpers.go index abba1a23c9..ef208cb907 100644 --- a/testutil/keeper/unit_test_helpers.go +++ b/testutil/keeper/unit_test_helpers.go @@ -215,6 +215,7 @@ func SetupForStoppingConsumerChain(t *testing.T, ctx sdk.Context, providerKeeper *providerkeeper.Keeper, mocks MockedKeepers, ) { t.Helper() + mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Times(1) expectations := GetMocksForCreateConsumerClient(ctx, &mocks, "chainID", clienttypes.NewHeight(4, 5)) expectations = append(expectations, GetMocksForSetConsumerChain(ctx, &mocks, "chainID")...) diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 2e8c9b46d4..e112e6b761 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -388,6 +388,19 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { continue } + consumerGenesis, found := k.GetConsumerGenesis(cachedCtx, prop.ChainId) + if !found { + // drop the proposal + ctx.Logger().Info("consumer genesis could not be created") + continue + } + + if len(consumerGenesis.Provider.InitialValSet) == 0 { + // drop the proposal + ctx.Logger().Info("consumer genesis initial validator set is empty - no validators opted in") + continue + } + // The cached context is created with a new EventManager so we merge the event // into the original context ctx.EventManager().EmitEvents(cachedCtx.EventManager().Events()) diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index 80b4531b75..84cb91acb8 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -3,6 +3,7 @@ package keeper_test import ( "bytes" "encoding/json" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "sort" "testing" "time" @@ -115,6 +116,7 @@ func TestHandleConsumerAdditionProposal(t *testing.T) { if tc.expAppendProp { // Mock calls are only asserted if we expect a client to be created. + mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Times(1) gomock.InOrder( testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, tc.prop.ChainId, clienttypes.NewHeight(2, 3))..., ) @@ -158,6 +160,7 @@ func TestCreateConsumerClient(t *testing.T) { description: "No state mutation, new client should be created", setup: func(providerKeeper *providerkeeper.Keeper, ctx sdk.Context, mocks *testkeeper.MockedKeepers) { // Valid client creation is asserted with mock expectations here + mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Times(1) gomock.InOrder( testkeeper.GetMocksForCreateConsumerClient(ctx, mocks, "chainID", clienttypes.NewHeight(4, 5))..., ) @@ -796,6 +799,7 @@ func TestMakeConsumerGenesis(t *testing.T) { // ctx = ctx.WithChainID("testchain1") // chainID is obtained from ctx ctx = ctx.WithBlockHeight(5) // RevisionHeight obtained from ctx + mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Times(1) gomock.InOrder(testkeeper.GetMocksForMakeConsumerGenesis(ctx, &mocks, 1814400000000000)...) // matches params from jsonString @@ -1005,6 +1009,22 @@ func TestBeginBlockInit(t *testing.T) { nil, nil, ).(*providertypes.ConsumerAdditionProposal), + providertypes.NewConsumerAdditionProposal( + "title", "opt-in chain with no validator opted in", "chain6", clienttypes.NewHeight(3, 4), []byte{}, []byte{}, + now.Add(-time.Minute).UTC(), + "0.75", + 10, + "", + 10000, + 100000000000, + 100000000000, + 100000000000, + 0, + 0, + 0, + nil, + nil, + ).(*providertypes.ConsumerAdditionProposal), } // Expect client creation for only the first, second, and fifth proposals (spawn time already passed and valid) @@ -1012,6 +1032,10 @@ func TestBeginBlockInit(t *testing.T) { expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain2", clienttypes.NewHeight(3, 4))...) expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain5", clienttypes.NewHeight(3, 4))...) + // The sixth proposal would have spawn time passed and hence needs the mocks but the client will not be + // created because `chain6` is an Opt In chain and has no validator opted in + expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain6", clienttypes.NewHeight(3, 4))...) + gomock.InOrder(expectedCalls...) for _, prop := range pendingProps { @@ -1021,6 +1045,8 @@ func TestBeginBlockInit(t *testing.T) { // opt in a sample validator so the chain's proposal can successfully execute validator := cryptotestutil.NewCryptoIdentityFromIntSeed(0).SDKStakingValidator() consAddr, _ := validator.GetConsAddr() + mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Return([]stakingtypes.Validator{validator}).AnyTimes() + mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), validator.GetOperator()).Return(int64(1)).AnyTimes() providerKeeper.SetOptedIn(ctx, pendingProps[4].ChainId, providertypes.NewProviderConsAddress(consAddr)) providerKeeper.BeginBlockInit(ctx) @@ -1061,10 +1087,30 @@ func TestBeginBlockInit(t *testing.T) { _, found = providerKeeper.GetPendingConsumerAdditionProp( ctx, pendingProps[4].SpawnTime, pendingProps[4].ChainId) require.False(t, found) - // sixth proposal was successfully executed and hence consumer genesis was created + // fifth proposal was successfully executed and hence consumer genesis was created _, found = providerKeeper.GetConsumerGenesis(ctx, pendingProps[4].ChainId) require.True(t, found) + // sixth proposal corresponds to an Opt-In chain with no opted-in validators and hence the + // proposal is not successful + _, found = providerKeeper.GetPendingConsumerAdditionProp( + ctx, pendingProps[5].SpawnTime, pendingProps[5].ChainId) + // the proposal was dropped and deleted + require.False(t, found) + // no consumer genesis is created + _, found = providerKeeper.GetConsumerGenesis(ctx, pendingProps[5].ChainId) + require.False(t, found) + // no consumer client is associated with this chain + _, found = providerKeeper.GetConsumerClientId(ctx, pendingProps[5].ChainId) + require.False(t, found) + // no fields should be set for this (check some of them) + _, found = providerKeeper.GetTopN(ctx, pendingProps[5].ChainId) + require.False(t, found) + _, found = providerKeeper.GetValidatorsPowerCap(ctx, pendingProps[5].ChainId) + require.False(t, found) + _, found = providerKeeper.GetValidatorSetCap(ctx, pendingProps[5].ChainId) + require.False(t, found) + // test that Top N is set correctly require.True(t, providerKeeper.IsTopN(ctx, "chain1")) topN, found := providerKeeper.GetTopN(ctx, "chain1") @@ -1104,6 +1150,7 @@ func TestBeginBlockCCR(t *testing.T) { expectations := []*gomock.Call{} for _, prop := range pendingProps { // A consumer chain is setup corresponding to each prop, making these mocks necessary + mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Times(1) expectations = append(expectations, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, prop.ChainId, clienttypes.NewHeight(2, 3))...) expectations = append(expectations, testkeeper.GetMocksForSetConsumerChain(ctx, &mocks, prop.ChainId)...) diff --git a/x/ccv/provider/proposal_handler_test.go b/x/ccv/provider/proposal_handler_test.go index 185db25b07..8d5b696b40 100644 --- a/x/ccv/provider/proposal_handler_test.go +++ b/x/ccv/provider/proposal_handler_test.go @@ -96,6 +96,7 @@ func TestProviderProposalHandler(t *testing.T) { // Mock expectations depending on expected outcome switch { case tc.expValidConsumerAddition: + mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Times(1) gomock.InOrder(testkeeper.GetMocksForCreateConsumerClient( ctx, &mocks, "chainID", clienttypes.NewHeight(2, 3), )...) From 26d68001273211850416dc8604bd2e5f5f57577f Mon Sep 17 00:00:00 2001 From: bernd-m <43466467+bermuell@users.noreply.github.com> Date: Fri, 17 May 2024 14:01:40 +0200 Subject: [PATCH 030/102] test: Remove v5.0.0 (pre-release) be tested within e2e compatibility (#1894) * Remove v5.0.0 (pre-release) from last version to be tested within e2e comopatibility tests * Apply suggestions from code review Co-authored-by: Marius Poke --------- Co-authored-by: Marius Poke --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 15fd06355d..255bba294a 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,8 @@ BRANCH := $(shell git rev-parse --abbrev-ref HEAD) COMMIT := $(shell git log -1 --format='%H') # Fetch tags and get the latest ICS version by filtering tags by vX.Y.Z and vX.Y.Z-lsm # using lazy set to only execute commands when variable is used -LATEST_RELEASE ?= $(shell git fetch; git tag -l --sort -v:refname 'v*.?' 'v*.?'-lsm 'v*.??' 'v*.??'-lsm | head -n 1) +# Note: v.5.0.0 is currently excluded from the list as it's a pre-release and will be added back once it's out of pre-release status +LATEST_RELEASE ?= $(shell git fetch; git tag -l --sort -v:refname 'v*.?' 'v*.?'-lsm 'v*.??' 'v*.??'-lsm --no-contains v5.0.0 | head -n 1) # don't override user values ifeq (,$(VERSION)) From a1557bb36d603043bb513362ddf2bc56fb6ae2d2 Mon Sep 17 00:00:00 2001 From: insumity Date: Fri, 17 May 2024 14:38:49 +0200 Subject: [PATCH 031/102] test: add E2E test for power-shaping features (#1853) * added E2E tests * Update tests/e2e/steps_partial_set_security.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * added to nightly tests --------- Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> --- .github/workflows/nightly-e2e.yml | 102 +++ tests/e2e/actions.go | 8 + tests/e2e/main.go | 26 + tests/e2e/steps_partial_set_security.go | 892 ++++++++++++++++++++++++ 4 files changed, 1028 insertions(+) diff --git a/.github/workflows/nightly-e2e.yml b/.github/workflows/nightly-e2e.yml index 7adbddf835..906b2a2806 100644 --- a/.github/workflows/nightly-e2e.yml +++ b/.github/workflows/nightly-e2e.yml @@ -181,6 +181,102 @@ jobs: go-version: "1.21" # The Go version to download (if necessary) and use. - name: E2E consumer-double-downtime tests run: go run ./tests/e2e/... --tc consumer-double-downtime + partial-set-security-opt-in-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - uses: actions/checkout@v4 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.21" # The Go version to download (if necessary) and use. + - name: E2E partial set security opt-in chain + run: go run ./tests/e2e/... --tc partial-set-security-opt-in + partial-set-security-top-n-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - uses: actions/checkout@v4 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.21" # The Go version to download (if necessary) and use. + - name: E2E partial set security Top N chain + run: go run ./tests/e2e/... --tc partial-set-security-top-n + partial-set-security-validator-set-cap-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - uses: actions/checkout@v4 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.21" # The Go version to download (if necessary) and use. + - name: E2E partial set security validator-set cap + run: go run ./tests/e2e/... --tc partial-set-security-validator-set-cap + partial-set-security-validators-power-cap-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - uses: actions/checkout@v4 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.21" # The Go version to download (if necessary) and use. + - name: E2E partial set security validators-power cap + run: go run ./tests/e2e/... --tc partial-set-security-validators-power-cap + partial-set-security-validators-allowlisted-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - uses: actions/checkout@v4 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.21" # The Go version to download (if necessary) and use. + - name: E2E partial set security allowlist + run: go run ./tests/e2e/... --tc partial-set-security-validators-allowlisted + partial-set-security-validators-denylisted-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - uses: actions/checkout@v4 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.21" # The Go version to download (if necessary) and use. + - name: E2E partial set security denylist + run: go run ./tests/e2e/... --tc partial-set-security-validators-denylisted nightly-test-fail: needs: @@ -193,6 +289,12 @@ jobs: - consumer-misbehaviour-test - consumer-double-sign-test - consumer-double-downtime-test + - partial-set-security-opt-in-test + - partial-set-security-top-n-test + - partial-set-security-validator-set-cap-test + - partial-set-security-validators-power-cap-test + - partial-set-security-validators-allowlisted-test + - partial-set-security-validators-denylisted-test if: ${{ failure() }} runs-on: ubuntu-latest steps: diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 1587608177..32e8604709 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -261,6 +261,10 @@ type SubmitConsumerAdditionProposalAction struct { InitialHeight clienttypes.Height DistributionChannel string TopN uint32 + ValidatorsPowerCap uint32 + ValidatorSetCap uint32 + Allowlist []string + Denylist []string } func (tr TestConfig) submitConsumerAdditionProposal( @@ -287,6 +291,10 @@ func (tr TestConfig) submitConsumerAdditionProposal( Deposit: fmt.Sprint(action.Deposit) + `stake`, DistributionTransmissionChannel: action.DistributionChannel, TopN: action.TopN, + ValidatorsPowerCap: action.ValidatorsPowerCap, + ValidatorSetCap: action.ValidatorSetCap, + Allowlist: action.Allowlist, + Denylist: action.Denylist, } bz, err := json.Marshal(prop) diff --git a/tests/e2e/main.go b/tests/e2e/main.go index c1766ccf37..9fb57f286a 100644 --- a/tests/e2e/main.go +++ b/tests/e2e/main.go @@ -168,6 +168,30 @@ var stepChoices = map[string]StepChoice{ description: "test partial set security for a Top-N chain", testConfig: DefaultTestCfg, }, + "partial-set-security-validator-set-cap": { + name: "partial-set-security-validator-set-cap", + steps: stepsValidatorSetCappedChain(), + description: "test partial set security for an Opt-In chain that is validator-set capped", + testConfig: DefaultTestCfg, + }, + "partial-set-security-validators-power-cap": { + name: "partial-set-security-validators-power-cap", + steps: stepsValidatorsPowerCappedChain(), + description: "test partial set security for an Opt-In chain that has its validators' power capped", + testConfig: DefaultTestCfg, + }, + "partial-set-security-validators-allowlisted": { + name: "partial-set-security-validators-allowlisted", + steps: stepsValidatorsAllowlistedChain(), + description: "test partial set security for an Opt-In chain that has some validators allowlisted", + testConfig: DefaultTestCfg, + }, + "partial-set-security-validators-denylisted": { + name: "partial-set-security-validators-denylisted", + steps: stepsValidatorsDenylistedChain(), + description: "test partial set security for an Opt-In chain that has a validator denylisted", + testConfig: DefaultTestCfg, + }, } func getTestCaseUsageString() string { @@ -254,6 +278,8 @@ func getTestCases(selectedPredefinedTests, selectedTestFiles TestSet, providerVe "democracy-reward", "democracy", "slash-throttle", "consumer-double-sign", "consumer-misbehaviour", "consumer-double-downtime", "partial-set-security-opt-in", "partial-set-security-top-n", + "partial-set-security-validator-set-cap", "partial-set-security-validators-power-cap", + "partial-set-security-validators-allowlisted", "partial-set-security-validators-denylisted", } if includeMultiConsumer != nil && *includeMultiConsumer { selectedPredefinedTests = append(selectedPredefinedTests, "multiconsumer") diff --git a/tests/e2e/steps_partial_set_security.go b/tests/e2e/steps_partial_set_security.go index eb551d80ed..5fa0880dcd 100644 --- a/tests/e2e/steps_partial_set_security.go +++ b/tests/e2e/steps_partial_set_security.go @@ -1000,3 +1000,895 @@ func stepsTopNChain() []Step { return s } + +// stepsValidatorSetCappedChain starts a provider chain and an Opt-In chain that is validator-set capped +func stepsValidatorSetCappedChain() []Step { + s := []Step{ + { + Action: StartChainAction{ + Chain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + }, + }, + { + Action: SubmitConsumerAdditionProposalAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + Deposit: 10000001, + ConsumerChain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + TopN: 0, + // we can have at most 2 validators validating the consumer chain + ValidatorSetCap: 2, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerAdditionProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_VOTING_PERIOD", + }, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + // Οpt in "alice", "bob", and "carol." Note, that "alice" and "bob" use the provider's public key + // (see `UseConsumerKey` is set to `false` in `getDefaultValidators`) and hence do not need a consumer-key assigment. + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("alice"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + // chain is not running yet and hence noone has to validate + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("bob"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + // assign the consumer key "carol" is using on the consumer chain to be the one "carol" uses when opting in + Action: AssignConsumerPubKeyAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + // reconfigure the node -> validator was using provider key + // until this point -> key matches config.consumerValPubKey for "carol" + ConsumerPubkey: getDefaultValidators()[ValidatorID("carol")].ConsumerValPubKey, + ReconfigureNode: true, + }, + State: State{}, + }, + { + Action: VoteGovProposalAction{ + Chain: ChainID("provi"), + From: []ValidatorID{ValidatorID("alice"), ValidatorID("bob")}, + Vote: []string{"yes", "yes"}, + PropNumber: 1, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerAdditionProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_PASSED", + }, + }, + }, + }, + }, + { + Action: StartConsumerChainAction{ + ConsumerChain: ChainID("consu"), + ProviderChain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + // For consumers that're launching with the provider being on an earlier version + // of ICS before the soft opt-out threshold was introduced, we need to set the + // soft opt-out threshold to 0.05 in the consumer genesis to ensure that the + // consumer binary doesn't panic. Sdk requires that all params are set to valid + // values from the genesis file. + GenesisChanges: ".app_state.ccvconsumer.params.soft_opt_out_threshold = \"0.05\"", + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + // alice does not validate because the consumer chain is validator-set capped to 2 validators and + // bob and carol have more power than alice + ValidatorID("alice"): 0, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + }, + }, + { + Action: AddIbcConnectionAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ClientA: 0, + ClientB: 0, + }, + State: State{}, + }, + { + Action: AddIbcChannelAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ConnectionA: 0, + PortA: "consumer", + PortB: "provider", + Order: "ordered", + }, + State: State{}, + }, + { + Action: OptOutAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 200, + // "carol" has opted out, but the VSCPacket capturing the opt-out was not relayed yet + ValidatorID("carol"): 300, + }, + }, + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {}, // "carol" does not have to validate anymore because it opted out + }, + }, + }, + }, + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + // "alice" is now validating the consumer chain as well because the chain is capped to 2 validators + // and "carol" just opted out + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + // "carol" has now opted out + ValidatorID("carol"): 0, + }, + }, + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + } + + return s +} + +// stepsValidatorsPowerCappedChain starts a provider chain and an Opt-In chain that is validators-power capped +func stepsValidatorsPowerCappedChain() []Step { + s := []Step{ + { + Action: StartChainAction{ + Chain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + }, + }, + { + Action: SubmitConsumerAdditionProposalAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + Deposit: 10000001, + ConsumerChain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + TopN: 0, + // Set the power cap to 34%. No validator can have more than 34% of the voting power on the consumer chain + ValidatorsPowerCap: 34, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerAdditionProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_VOTING_PERIOD", + }, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + // Οpt in "alice", "bob", and "carol." Note, that "alice" and "bob" use the provider's public key + // (see `UseConsumerKey` is set to `false` in `getDefaultValidators`) and hence do not need a consumer-key assigment. + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("alice"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, // chain is not running yet + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("bob"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + // assign the consumer key "carol" is using on the consumer chain to be the one "carol" uses when opting in + Action: AssignConsumerPubKeyAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + // reconfigure the node -> validator was using provider key + // until this point -> key matches config.consumerValPubKey for "carol" + ConsumerPubkey: getDefaultValidators()[ValidatorID("carol")].ConsumerValPubKey, + ReconfigureNode: true, + }, + State: State{}, + }, + { + Action: VoteGovProposalAction{ + Chain: ChainID("provi"), + From: []ValidatorID{ValidatorID("alice"), ValidatorID("bob")}, + Vote: []string{"yes", "yes"}, + PropNumber: 1, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerAdditionProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_PASSED", + }, + }, + }, + }, + }, + { + Action: StartConsumerChainAction{ + ConsumerChain: ChainID("consu"), + ProviderChain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + // For consumers that're launching with the provider being on an earlier version + // of ICS before the soft opt-out threshold was introduced, we need to set the + // soft opt-out threshold to 0.05 in the consumer genesis to ensure that the + // consumer binary doesn't panic. Sdk requires that all params are set to valid + // values from the genesis file. + GenesisChanges: ".app_state.ccvconsumer.params.soft_opt_out_threshold = \"0.05\"", + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + // the powers of the validators on the consumer chain are different from the provider chain + // because the consumer chain is power capped. Note that the total power is 600 (= 194 + 203 + 203) + // and 203 / 600.0 = 0.338 < 34% that is the power cap. + ValidatorID("alice"): 194, + ValidatorID("bob"): 203, + ValidatorID("carol"): 203, + }, + }, + }, + }, + { + Action: AddIbcConnectionAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ClientA: 0, + ClientB: 0, + }, + State: State{}, + }, + { + Action: AddIbcChannelAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ConnectionA: 0, + PortA: "consumer", + PortB: "provider", + Order: "ordered", + }, + State: State{}, + }, + { + Action: OptOutAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 194, + ValidatorID("bob"): 203, + ValidatorID("carol"): 203, + }, + }, + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {}, // "carol" does not have to validate anymore because it opted out + }, + }, + }, + }, + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + // "carol" has opted out, and we only have 2 validators left validating. Power capping only operates + // in a best-effort basis and with 2 validators we cannot guarantee that no validator has more + // than 34% of the voting power. In this case, both validators get the same voting power (50% = 101 / 202). + ValidatorID("alice"): 101, + ValidatorID("bob"): 101, + ValidatorID("carol"): 0, + }, + }, + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + } + + return s +} + +// stepsValidatorsAllowlistedChain starts a provider chain and an Opt-In chain with an allowlist +func stepsValidatorsAllowlistedChain() []Step { + s := []Step{ + { + Action: StartChainAction{ + Chain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + }, + }, + { + Action: SubmitConsumerAdditionProposalAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + Deposit: 10000001, + ConsumerChain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + TopN: 0, + // only "alice" and "bob" are allowlisted (see `getDefaultValidators` in `tests/e2e/config.go`) + Allowlist: []string{"cosmosvalcons1qmq08eruchr5sf5s3rwz7djpr5a25f7xw4mceq", + "cosmosvalcons1nx7n5uh0ztxsynn4sje6eyq2ud6rc6klc96w39"}, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerAdditionProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_VOTING_PERIOD", + }, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + // Οpt in "alice", "bob", and "carol." Note, that "alice" and "bob" use the provider's public key + // (see `UseConsumerKey` is set to `false` in `getDefaultValidators`) and hence do not need a consumer-key assigment. + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("alice"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, // chain is not running yet + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("bob"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + // assign the consumer key "carol" is using on the consumer chain to be the one "carol" uses when opting in + Action: AssignConsumerPubKeyAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + // reconfigure the node -> validator was using provider key + // until this point -> key matches config.consumerValPubKey for "carol" + ConsumerPubkey: getDefaultValidators()[ValidatorID("carol")].ConsumerValPubKey, + ReconfigureNode: true, + }, + State: State{}, + }, + { + Action: VoteGovProposalAction{ + Chain: ChainID("provi"), + From: []ValidatorID{ValidatorID("alice"), ValidatorID("bob")}, + Vote: []string{"yes", "yes"}, + PropNumber: 1, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerAdditionProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_PASSED", + }, + }, + }, + }, + }, + { + Action: StartConsumerChainAction{ + ConsumerChain: ChainID("consu"), + ProviderChain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + // For consumers that're launching with the provider being on an earlier version + // of ICS before the soft opt-out threshold was introduced, we need to set the + // soft opt-out threshold to 0.05 in the consumer genesis to ensure that the + // consumer binary doesn't panic. Sdk requires that all params are set to valid + // values from the genesis file. + GenesisChanges: ".app_state.ccvconsumer.params.soft_opt_out_threshold = \"0.05\"", + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + // "carol" is not allowlisted and hence does not validate the consumer chain + ValidatorID("carol"): 0, + }, + }, + }, + }, + { + Action: AddIbcConnectionAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ClientA: 0, + ClientB: 0, + }, + State: State{}, + }, + { + Action: AddIbcChannelAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ConnectionA: 0, + PortA: "consumer", + PortB: "provider", + Order: "ordered", + }, + State: State{}, + }, + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 0, + }, + }, + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + } + + return s +} + +// stepsValidatorsDenylistedChain starts a provider chain and an Opt-In chain with a denylist +func stepsValidatorsDenylistedChain() []Step { + s := []Step{ + { + Action: StartChainAction{ + Chain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + }, + }, + { + Action: SubmitConsumerAdditionProposalAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + Deposit: 10000001, + ConsumerChain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + TopN: 0, + // only "bob" is denylisted (see `getDefaultValidators` in `tests/e2e/config.go`) + Denylist: []string{"cosmosvalcons1nx7n5uh0ztxsynn4sje6eyq2ud6rc6klc96w39"}, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerAdditionProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_VOTING_PERIOD", + }, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + // Οpt in "alice", "bob", and "carol." Note, that "alice" and "bob" use the provider's public key + // (see `UseConsumerKey` is set to `false` in `getDefaultValidators`) and hence do not need a consumer-key assigment. + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("alice"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, // chain is not running yet + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("bob"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + // assign the consumer key "carol" is using on the consumer chain to be the one "carol" uses when opting in + Action: AssignConsumerPubKeyAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + // reconfigure the node -> validator was using provider key + // until this point -> key matches config.consumerValPubKey for "carol" + ConsumerPubkey: getDefaultValidators()[ValidatorID("carol")].ConsumerValPubKey, + ReconfigureNode: true, + }, + State: State{}, + }, + { + Action: VoteGovProposalAction{ + Chain: ChainID("provi"), + From: []ValidatorID{ValidatorID("alice"), ValidatorID("bob")}, + Vote: []string{"yes", "yes"}, + PropNumber: 1, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerAdditionProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_PASSED", + }, + }, + }, + }, + }, + { + Action: StartConsumerChainAction{ + ConsumerChain: ChainID("consu"), + ProviderChain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + // For consumers that're launching with the provider being on an earlier version + // of ICS before the soft opt-out threshold was introduced, we need to set the + // soft opt-out threshold to 0.05 in the consumer genesis to ensure that the + // consumer binary doesn't panic. Sdk requires that all params are set to valid + // values from the genesis file. + GenesisChanges: ".app_state.ccvconsumer.params.soft_opt_out_threshold = \"0.05\"", + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + // "bob" is denylisted and hence does not valiate the consumer chain + ValidatorID("bob"): 0, + ValidatorID("carol"): 300, + }, + }, + }, + }, + { + Action: AddIbcConnectionAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ClientA: 0, + ClientB: 0, + }, + State: State{}, + }, + { + Action: AddIbcChannelAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ConnectionA: 0, + PortA: "consumer", + PortB: "provider", + Order: "ordered", + }, + State: State{}, + }, + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 0, + ValidatorID("carol"): 300, + }, + }, + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + } + + return s +} From 36a2876963aa57d5e5da9dcb8d3b1da286a5a8f5 Mon Sep 17 00:00:00 2001 From: Marius Poke Date: Fri, 17 May 2024 15:28:36 +0200 Subject: [PATCH 032/102] build(deps): bump comet to v0.37.6 (#1876) * bump comet to v0.37.6 * add changelog entry * fix exp deps and pin it to go.mod * bump comet to 0.37.5 and sdk to 0.47.11 to match * bump comet back to 0.37.6 * add changelog entry for SDK --------- Co-authored-by: MSalopek --- .../dependencies/1876-bump-comet.md | 3 + .../unreleased/dependencies/1876-bump-sdk.md | 3 + go.mod | 32 +++--- go.sum | 105 ++++++------------ 4 files changed, 61 insertions(+), 82 deletions(-) create mode 100644 .changelog/unreleased/dependencies/1876-bump-comet.md create mode 100644 .changelog/unreleased/dependencies/1876-bump-sdk.md diff --git a/.changelog/unreleased/dependencies/1876-bump-comet.md b/.changelog/unreleased/dependencies/1876-bump-comet.md new file mode 100644 index 0000000000..aed8c4f155 --- /dev/null +++ b/.changelog/unreleased/dependencies/1876-bump-comet.md @@ -0,0 +1,3 @@ +- Bump [CometBFT](https://github.com/cometbft/cometbft) to + [v0.37.6](https://github.com/cometbft/cometbft/releases/tag/v0.37.6). + ([\#1876](https://github.com/cosmos/interchain-security/pull/1876)) \ No newline at end of file diff --git a/.changelog/unreleased/dependencies/1876-bump-sdk.md b/.changelog/unreleased/dependencies/1876-bump-sdk.md new file mode 100644 index 0000000000..8f18db0eca --- /dev/null +++ b/.changelog/unreleased/dependencies/1876-bump-sdk.md @@ -0,0 +1,3 @@ +- Bump [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) to + [v0.47.11](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.47.11). + ([\#1876](https://github.com/cosmos/interchain-security/pull/1876)) \ No newline at end of file diff --git a/go.mod b/go.mod index ffc4822264..7e49b58b52 100644 --- a/go.mod +++ b/go.mod @@ -2,14 +2,12 @@ module github.com/cosmos/interchain-security/v4 go 1.21.1 -toolchain go1.21.6 - require ( cosmossdk.io/errors v1.0.1 cosmossdk.io/math v1.3.0 - github.com/cometbft/cometbft v0.37.4 + github.com/cometbft/cometbft v0.37.6 github.com/cometbft/cometbft-db v0.11.0 - github.com/cosmos/cosmos-sdk v0.47.10 + github.com/cosmos/cosmos-sdk v0.47.11 github.com/cosmos/gogoproto v1.4.10 github.com/cosmos/ibc-go/v7 v7.4.0 github.com/cosmos/ics23/go v0.10.0 @@ -25,7 +23,7 @@ require ( github.com/stretchr/testify v1.9.0 github.com/tidwall/gjson v1.17.1 golang.org/x/crypto v0.21.0 // indirect - golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 golang.org/x/net v0.23.0 // indirect golang.org/x/sys v0.18.0 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect @@ -70,7 +68,7 @@ require ( github.com/cosmos/rosetta-sdk-go v0.10.0 // indirect github.com/creachadair/taskgroup v0.4.2 // indirect github.com/danieljoos/wincred v1.1.2 // indirect - github.com/davecgh/go-spew v1.1.1 + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect @@ -79,7 +77,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.6.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect @@ -115,7 +113,7 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/compress v1.17.0 // indirect github.com/lib/pq v1.10.7 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/linxGnu/grocksdb v1.8.12 // indirect @@ -129,10 +127,10 @@ require ( github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mtibben/percent v0.2.1 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect @@ -140,10 +138,9 @@ require ( github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rs/cors v1.8.3 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect - github.com/spf13/afero v1.9.5 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/afero v1.11.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/subosito/gotenv v1.4.2 // 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.6.0 // indirect @@ -168,7 +165,7 @@ require ( require ( github.com/informalsystems/itf-go v0.0.1 - github.com/spf13/viper v1.16.0 + github.com/spf13/viper v1.18.2 golang.org/x/mod v0.17.0 google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de ) @@ -190,12 +187,17 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/rs/zerolog v1.32.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect go.opentelemetry.io/otel v1.22.0 // indirect go.opentelemetry.io/otel/metric v1.22.0 // indirect go.opentelemetry.io/otel/trace v1.22.0 // indirect + go.uber.org/atomic v1.10.0 // indirect go.uber.org/mock v0.2.0 // indirect + go.uber.org/multierr v1.9.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect @@ -203,3 +205,5 @@ require ( // following versions might cause unexpected behavior replace github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 + +replace golang.org/x/exp => golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb diff --git a/go.sum b/go.sum index a41e43b3d6..e7a05546aa 100644 --- a/go.sum +++ b/go.sum @@ -3,7 +3,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT 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.44.3/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= @@ -16,7 +15,6 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY 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.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= @@ -170,7 +168,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo 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.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= 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= @@ -201,7 +198,6 @@ cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= cosmossdk.io/tools/rosetta v0.2.1 h1:ddOMatOH+pbxWbrGJKRAawdBkPYLfKXutK9IETnjYxw= cosmossdk.io/tools/rosetta v0.2.1/go.mod h1:Pqdc1FdvkNV3LcNIkYWt2RQY6IP1ge6YWZk8MhhO9Hw= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= @@ -211,15 +207,14 @@ github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwR github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRrLeDnvGIM= github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= -github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= @@ -324,8 +319,8 @@ github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1: github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coinbase/rosetta-sdk-go/types v1.0.0 h1:jpVIwLcPoOeCR6o1tU+Xv7r5bMONNbHU7MuEHboiFuA= github.com/coinbase/rosetta-sdk-go/types v1.0.0/go.mod h1:eq7W2TMRH22GTW0N0beDnN931DW0/WOI1R2sdHNHG4c= -github.com/cometbft/cometbft v0.37.4 h1:xyvvEqlyfK8MgNIIKVJaMsuIp03wxOcFmVkT26+Ikpg= -github.com/cometbft/cometbft v0.37.4/go.mod h1:Cmg5Hp4sNpapm7j+x0xRyt2g0juQfmB752ous+pA0G8= +github.com/cometbft/cometbft v0.37.6 h1:2BSD0lGPbcIyRd99Pf1zH0Sa8o0pbfqVWEDbZ4Ec2Uc= +github.com/cometbft/cometbft v0.37.6/go.mod h1:5FRkFil9uagHZogIX9x8z51c3GIPpQmdIN8Mq46HfzY= github.com/cometbft/cometbft-db v0.11.0 h1:M3Lscmpogx5NTbb1EGyGDaFRdsoLWrUWimFEyf7jej8= github.com/cometbft/cometbft-db v0.11.0/go.mod h1:GDPJAC/iFHNjmZZPN8V8C1yr/eyityhi2W1hz2MGKSc= github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4= @@ -342,8 +337,8 @@ github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= github.com/cosmos/cosmos-proto v1.0.0-beta.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.47.10 h1:Wxf5yEN3jZbG4fftxAMKB6rpd8ME0mxuCVihpz65dt0= -github.com/cosmos/cosmos-sdk v0.47.10/go.mod h1:UWpgWkhcsBIATS68uUC0del7IiBN4hPv/vqg8Zz23uw= +github.com/cosmos/cosmos-sdk v0.47.11 h1:0Qx7eORw0RJqPv+mvDuU8NQ1LV3nJJKJnPoYblWHolc= +github.com/cosmos/cosmos-sdk v0.47.11/go.mod h1:ADjORYzUQqQv/FxDi0H0K5gW/rAk1CiDR3ZKsExfJV0= 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= @@ -372,8 +367,9 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/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/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= @@ -428,8 +424,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk 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.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +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.23.0 h1:dn+QRCeJv4pPt9OjVXiMcGIBIefaTJPw/h0bZWO05nE= github.com/getsentry/sentry-go v0.23.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -440,9 +436,6 @@ github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= 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= @@ -579,7 +572,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf 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-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -610,7 +602,6 @@ github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMd github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= @@ -725,11 +716,10 @@ github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -854,8 +844,8 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= 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-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU= @@ -870,9 +860,9 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/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= @@ -927,6 +917,10 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR 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.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +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= @@ -942,12 +936,14 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 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.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +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.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= @@ -956,15 +952,13 @@ github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tL github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/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.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= -github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= 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= @@ -983,11 +977,11 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +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.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= -github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +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= @@ -1057,10 +1051,14 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe 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.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/mock v0.2.0 h1:TaP3xedm7JaAgScZO7tlvlKrqT0p7I6OsdGB5YNSMDU= go.uber.org/mock v0.2.0/go.mod h1:J0y0rp9L3xiff1+ZBfKxlC1fz2+aO16tw0tsDOixfuM= 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.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= 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= @@ -1075,26 +1073,12 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U 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-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -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-20230711153332-06a737ee72cb h1:xIApU0ow1zwMa2uL1VDNeQlNVFTWMQxZUZCMDy0Q4Us= golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -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= @@ -1107,12 +1091,8 @@ golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPI 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= @@ -1120,6 +1100,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1163,7 +1145,6 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= @@ -1171,7 +1152,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b 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-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/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= @@ -1240,7 +1220,6 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-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= @@ -1251,7 +1230,6 @@ golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7w 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= @@ -1285,13 +1263,11 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w 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-20210225134936-a50acf3fe073/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-20210423185535-09eb48e85fd7/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= @@ -1323,7 +1299,6 @@ golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBc 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-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1361,7 +1336,6 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm 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= @@ -1370,9 +1344,7 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn 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-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1387,7 +1359,6 @@ golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapK 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= @@ -1408,7 +1379,6 @@ golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4f 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.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -1417,6 +1387,7 @@ 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.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1527,10 +1498,8 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= From a4ffa1a3accbf6cfeec77a47709d496e5d0e9a51 Mon Sep 17 00:00:00 2001 From: MSalopek Date: Fri, 17 May 2024 17:08:31 +0200 Subject: [PATCH 033/102] docs: add v4.2.0; bump v5.0.0 (#1900) * docs: add v4.2.0; bump v5.0.0 * docs: add v4.2.0; bump v5.0.0 --- docs/docusaurus.config.js | 12 ------------ docs/versions.json | 5 ++--- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 5c85a14825..53678250fc 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -39,24 +39,12 @@ const config = { docs: { sidebarPath: require.resolve("./sidebars.js"), routeBasePath: "/", - lastVersion: "v4.1.0", versions: { current: { path: "/", label: "main", banner: "unreleased", }, - "v4.0.0": { - path: "/v4.0.0/", - banner: "none", - }, - "v4.1.0": { - path: "/v4.1.0/", - banner: "none", - }, - "v5.0.0-rc0": { - banner: "unreleased", - }, }, remarkPlugins: [remarkMath], rehypePlugins: [rehypeKatex], diff --git a/docs/versions.json b/docs/versions.json index e0778f2e0e..4d806f8d12 100644 --- a/docs/versions.json +++ b/docs/versions.json @@ -1,5 +1,4 @@ [ - "v4.0.0", - "v4.1.0", - "v5.0.0-rc0" + "v4.2.0", + "v5.0.0" ] \ No newline at end of file From 5f864a5b6aaa5d0d91989c7401b8850712300e55 Mon Sep 17 00:00:00 2001 From: MSalopek Date: Fri, 17 May 2024 17:46:20 +0200 Subject: [PATCH 034/102] docs: fix broken docs deploy (v4.2.0) (#1903) * docs: add v4.2.0; bump v5.0.0 * docs: add v4.2.0; bump v5.0.0 * sync * sync stuff * fix * try docs tag * add version alias --- docs/docusaurus.config.js | 8 ++++++++ docs/versions.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 53678250fc..c949763429 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -45,6 +45,14 @@ const config = { label: "main", banner: "unreleased", }, + "v4.2.0-docs": { + path: "/v4.2.0/", + label: "v4.2.0", + banner: "none", + }, + "v5.0.0": { + banner: "unreleased", + }, }, remarkPlugins: [remarkMath], rehypePlugins: [rehypeKatex], diff --git a/docs/versions.json b/docs/versions.json index 4d806f8d12..bba35efde5 100644 --- a/docs/versions.json +++ b/docs/versions.json @@ -1,4 +1,4 @@ [ - "v4.2.0", + "v4.2.0-docs", "v5.0.0" ] \ No newline at end of file From adab6dfbfa041bfda3fa15ecff5917872e036654 Mon Sep 17 00:00:00 2001 From: Cosmos SDK <113218068+github-prbot@users.noreply.github.com> Date: Wed, 22 May 2024 14:53:52 +0200 Subject: [PATCH 035/102] chore: fix spelling errors (#1904) chore: spelling errors fixes Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com> --- tests/e2e/steps_partial_set_security.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/e2e/steps_partial_set_security.go b/tests/e2e/steps_partial_set_security.go index 5fa0880dcd..164ff0d067 100644 --- a/tests/e2e/steps_partial_set_security.go +++ b/tests/e2e/steps_partial_set_security.go @@ -1055,7 +1055,7 @@ func stepsValidatorSetCappedChain() []Step { }, }, // Οpt in "alice", "bob", and "carol." Note, that "alice" and "bob" use the provider's public key - // (see `UseConsumerKey` is set to `false` in `getDefaultValidators`) and hence do not need a consumer-key assigment. + // (see `UseConsumerKey` is set to `false` in `getDefaultValidators`) and hence do not need a consumer-key assignment. { Action: OptInAction{ Chain: ChainID("consu"), @@ -1064,7 +1064,7 @@ func stepsValidatorSetCappedChain() []Step { State: State{ ChainID("provi"): ChainState{ HasToValidate: &map[ValidatorID][]ChainID{ - // chain is not running yet and hence noone has to validate + // chain is not running yet and hence no one has to validate ValidatorID("alice"): {}, ValidatorID("bob"): {}, ValidatorID("carol"): {}, @@ -1292,7 +1292,7 @@ func stepsValidatorsPowerCappedChain() []Step { }, }, // Οpt in "alice", "bob", and "carol." Note, that "alice" and "bob" use the provider's public key - // (see `UseConsumerKey` is set to `false` in `getDefaultValidators`) and hence do not need a consumer-key assigment. + // (see `UseConsumerKey` is set to `false` in `getDefaultValidators`) and hence do not need a consumer-key assignment. { Action: OptInAction{ Chain: ChainID("consu"), @@ -1529,7 +1529,7 @@ func stepsValidatorsAllowlistedChain() []Step { }, }, // Οpt in "alice", "bob", and "carol." Note, that "alice" and "bob" use the provider's public key - // (see `UseConsumerKey` is set to `false` in `getDefaultValidators`) and hence do not need a consumer-key assigment. + // (see `UseConsumerKey` is set to `false` in `getDefaultValidators`) and hence do not need a consumer-key assignment. { Action: OptInAction{ Chain: ChainID("consu"), @@ -1738,7 +1738,7 @@ func stepsValidatorsDenylistedChain() []Step { }, }, // Οpt in "alice", "bob", and "carol." Note, that "alice" and "bob" use the provider's public key - // (see `UseConsumerKey` is set to `false` in `getDefaultValidators`) and hence do not need a consumer-key assigment. + // (see `UseConsumerKey` is set to `false` in `getDefaultValidators`) and hence do not need a consumer-key assignment. { Action: OptInAction{ Chain: ChainID("consu"), From f8792084d33dfe9c2be2719954c9402ffe70f5d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 May 2024 14:57:31 +0200 Subject: [PATCH 036/102] build(deps): bump JamesIves/github-pages-deploy-action from 4.6.0 to 4.6.1 (#1905) build(deps): bump JamesIves/github-pages-deploy-action Bumps [JamesIves/github-pages-deploy-action](https://github.com/jamesives/github-pages-deploy-action) from 4.6.0 to 4.6.1. - [Release notes](https://github.com/jamesives/github-pages-deploy-action/releases) - [Commits](https://github.com/jamesives/github-pages-deploy-action/compare/v4.6.0...v4.6.1) --- updated-dependencies: - dependency-name: JamesIves/github-pages-deploy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/deploy-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index b8ff6c17c8..db8ecca694 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -42,7 +42,7 @@ jobs: ./build_deploy.sh - name: Deploy 🚀 - uses: JamesIves/github-pages-deploy-action@v4.6.0 + uses: JamesIves/github-pages-deploy-action@v4.6.1 with: branch: gh-pages folder: ~/output From 7fba468a0243b5a432edc60e0e035e4b25ef02c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 May 2024 14:57:52 +0200 Subject: [PATCH 037/102] build(deps): bump bufbuild/buf-setup-action from 1.31.0 to 1.32.0 (#1906) Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.31.0 to 1.32.0. - [Release notes](https://github.com/bufbuild/buf-setup-action/releases) - [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.31.0...v1.32.0) --- updated-dependencies: - dependency-name: bufbuild/buf-setup-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/proto-registry.yml | 2 +- .github/workflows/proto.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/proto-registry.yml b/.github/workflows/proto-registry.yml index 9938e96d22..ee9b317a22 100644 --- a/.github/workflows/proto-registry.yml +++ b/.github/workflows/proto-registry.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: bufbuild/buf-setup-action@v1.31.0 + - uses: bufbuild/buf-setup-action@v1.32.0 - uses: bufbuild/buf-push-action@v1 with: input: "proto" diff --git a/.github/workflows/proto.yml b/.github/workflows/proto.yml index 5e218cbb84..646655689f 100644 --- a/.github/workflows/proto.yml +++ b/.github/workflows/proto.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: bufbuild/buf-setup-action@v1.31.0 + - uses: bufbuild/buf-setup-action@v1.32.0 - uses: bufbuild/buf-breaking-action@v1 with: input: "proto" From 369554c674e0f7cca837ee3afcca08a7fb45db64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 May 2024 16:06:10 +0200 Subject: [PATCH 038/102] build(deps): bump google.golang.org/grpc from 1.63.2 to 1.64.0 (#1908) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.63.2 to 1.64.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.63.2...v1.64.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 28 ++++++++++++------------ go.sum | 67 ++++++++++++++++++++++++++++------------------------------ 2 files changed, 46 insertions(+), 49 deletions(-) diff --git a/go.mod b/go.mod index 7e49b58b52..82b65dd7f6 100644 --- a/go.mod +++ b/go.mod @@ -27,17 +27,17 @@ require ( golang.org/x/net v0.23.0 // indirect golang.org/x/sys v0.18.0 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/grpc v1.63.2 + google.golang.org/grpc v1.64.0 google.golang.org/protobuf v1.34.1 gopkg.in/yaml.v2 v2.4.0 ) require ( - cloud.google.com/go v0.112.0 // indirect - cloud.google.com/go/compute v1.24.0 // indirect + cloud.google.com/go v0.112.1 // indirect + cloud.google.com/go/compute v1.25.1 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.6 // indirect - cloud.google.com/go/storage v1.36.0 // indirect + cloud.google.com/go/storage v1.38.0 // indirect cosmossdk.io/api v0.3.1 cosmossdk.io/core v0.5.1 // indirect cosmossdk.io/depinject v1.0.0-alpha.4 // indirect @@ -92,7 +92,7 @@ require ( github.com/google/orderedcode v0.0.1 // 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.0 // indirect + github.com/googleapis/gax-go/v2 v2.12.2 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect @@ -151,10 +151,10 @@ require ( github.com/zondax/ledger-go v0.14.3 // indirect go.etcd.io/bbolt v1.3.8 // indirect go.opencensus.io v0.24.0 // indirect - golang.org/x/oauth2 v0.17.0 // indirect + golang.org/x/oauth2 v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect - google.golang.org/api v0.162.0 // indirect + google.golang.org/api v0.169.0 // indirect google.golang.org/appengine v1.6.8 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect @@ -167,7 +167,7 @@ require ( github.com/informalsystems/itf-go v0.0.1 github.com/spf13/viper v1.18.2 golang.org/x/mod v0.17.0 - google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de + google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 ) require ( @@ -190,17 +190,17 @@ require ( github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect - go.opentelemetry.io/otel v1.22.0 // indirect - go.opentelemetry.io/otel/metric v1.22.0 // indirect - go.opentelemetry.io/otel/trace v1.22.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/atomic v1.10.0 // indirect go.uber.org/mock v0.2.0 // indirect go.uber.org/multierr v1.9.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/time v0.5.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect ) // following versions might cause unexpected behavior diff --git a/go.sum b/go.sum index e7a05546aa..7c9ecfc0d9 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,8 @@ cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w9 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.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= -cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= +cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= +cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= 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= @@ -68,8 +68,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz 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 v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= -cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= +cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU= +cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= @@ -171,8 +171,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 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.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= -cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= +cloud.google.com/go/storage v1.38.0 h1:Az68ZRGlnNTpIBbLjSMIV2BDcwwXYlRlQzis0llkpJg= +cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY= 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= @@ -299,8 +299,6 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= 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= @@ -410,8 +408,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m 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/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= -github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -599,8 +595,8 @@ github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99 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.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= -github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/googleapis/gax-go/v2 v2.12.2 h1:mhN09QQW1jEWeMF74zGR81R30z4VJzjZsfkUhuHF+DA= +github.com/googleapis/gax-go/v2 v2.12.2/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= 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= @@ -1035,18 +1031,18 @@ 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.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= -go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= -go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= -go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= -go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= -go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= -go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= -go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= +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.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= +go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= +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= @@ -1191,8 +1187,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri 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.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= -golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= 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= @@ -1397,8 +1393,9 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T 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 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= 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= @@ -1448,8 +1445,8 @@ google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ 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.162.0 h1:Vhs54HkaEpkMBdgGdOT2P6F0csGG/vxDS0hWHJzmmps= -google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= +google.golang.org/api v0.169.0 h1:QwWPy71FgMWqJN/l6jVlFHUa29a7dcUy02I8o799nPY= +google.golang.org/api v0.169.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg= 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= @@ -1568,10 +1565,10 @@ google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= -google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= 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= @@ -1613,8 +1610,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu 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.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= 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= From 417c8990da207f9f274c61c5ac930b27629f2686 Mon Sep 17 00:00:00 2001 From: MSalopek Date: Fri, 24 May 2024 14:21:58 +0200 Subject: [PATCH 039/102] docs: document democracy modules in more detail (#1915) * docs: document democracy modules in more detail * docs: add diff to config * Update docs/docs/features/democracy-modules.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update docs/docs/features/democracy-modules.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update docs/docs/features/democracy-modules.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * cleanup after applying bot comments --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- docs/docs/features/democracy-modules.md | 521 ++++++++++++++++++++++++ docs/docusaurus.config.js | 2 +- 2 files changed, 522 insertions(+), 1 deletion(-) create mode 100644 docs/docs/features/democracy-modules.md diff --git a/docs/docs/features/democracy-modules.md b/docs/docs/features/democracy-modules.md new file mode 100644 index 0000000000..a138bb0413 --- /dev/null +++ b/docs/docs/features/democracy-modules.md @@ -0,0 +1,521 @@ +# Democracy modules + +This section is relevant for chains transitioning from a standalone chain and new consumer chains that require some functionality from the `x/staking` module. + +The democracy modules comprise `x/staking`, `x/distribution` and `x/governance` with overrides and extensions required for normal operation when participating in interchain security. + +The modules are plug-and-play and only require small wiring changes to be enabled. + +For a full integration check the `consumer-democracy` [example app](https://github.com/cosmos/interchain-security/blob/main/app/consumer-democracy/app.go). + +## Staking + +The democracy staking module allows the cosmos-sdk `x/staking` module to be used alongside the interchain security `consumer` module. + +The module uses overrides that allow the full `x/staking` functionality with one notable difference - the staking module will no longer be used to provide the consensus validator set. + +### Implications for consumer chains + +The `x/ccv/democracy/staking` allows consumer chains to separate governance from block production. + +:::info +The validator set coming from the provider chain does not need to participate in governance - they only provide infrastructure (create blocks and maintain consensus). +::: + +#### Governators (aka. Governors) + +Validators registered with the `x/staking` module become "Governators". + +Unlike Validators, Governators are not required to run any chain infastructure since they are not signing any blocks. + +However, Governators retain a subset of the validator properties: + +- new Governators can be created (via `MsgCreateValidator`) +- Governators can accept delegations +- Governators can vote on governance proposals (with their self stake and delegations) +- Governators earn token rewards + +With these changes, Governators can become community advocates that can specialize in chain governance and they get rewarded for their participation the same way the validators do. + +Additionally, Governators can choose to provide additional infrastructure such as RPC/API access points, archive nodes, indexers and similar software. + +#### Tokenomics + +The consumer chain's token will remain a governance and reward token. The token's parameters (inflation, max supply, burn rate) are not affected. + +:::info +Staking rewards are distributed to all Governators and their delegators after distributing the rewards to the provider chain's validator set. +::: + +### Integration + +The `x/ccv/democracy/staking` module provides these `x/staking` overrides: + +```golang + +// InitGenesis delegates the InitGenesis call to the underlying x/staking module, +// however, it returns no validator updates as validators are tracked via the +// consumer chain's x/cvv/consumer module and so this module is not responsible for returning the initial validator set. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + + cdc.MustUnmarshalJSON(data, &genesisState) + _ = am.keeper.InitGenesis(ctx, &genesisState) // run staking InitGenesis + + return []abci.ValidatorUpdate{} // do not return validator updates +} + +// EndBlock delegates the EndBlock call to the underlying x/staking module. +// However, no validator updates are returned as validators are tracked via the +// consumer chain's x/cvv/consumer module. +func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + _ = am.keeper.BlockValidatorUpdates(ctx) // perform staking BlockValidatorUpdates + return []abci.ValidatorUpdate{} // do not return validator updates +} +``` + +To integrate the `democracy/staking` follow this guide: + +#### 1. confirm that no modules are returning validator updates + +:::tip +Only the `x/ccv/consumer` module should be returning validator updates. +::: + +If some of your modules are returning validator updates please disable them while maintaining your business logic: + +```diff +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + + cdc.MustUnmarshalJSON(data, &genesisState) +- return am.keeper.InitGenesis(ctx, &genesisState) ++ _ = am.keeper.InitGenesis(ctx, &genesisState) // run InitGenesis but drop the result ++ return []abci.ValidatorUpdate{} // return empty validator updates +} + + +func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { +- return am.keeper.BlockValidatorUpdates(ctx) ++ _ = am.keeper.BlockValidatorUpdates(ctx) // perform staking BlockValidatorUpdates ++ return []abci.ValidatorUpdate{} // return empty validator updates +} +``` + +#### 2. wire the module in app.go + +You **do not need to remove** the cosmos-sdk `StakingKeeper` from your wiring. + +```diff +import ( + ... ++ ccvstaking "github.com/cosmos/interchain-security/v4/x/ccv/democracy/staking" +) + +var ( + // replace the staking.AppModuleBasic + ModuleBasics = module.NewBasicManager( + auth.AppModuleBasic{}, + genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator), + bank.AppModuleBasic{}, + capability.AppModuleBasic{}, +- sdkstaking.AppModuleBasic{}, ++ ccvstaking.AppModuleBasic{}, // replace sdkstaking + ... + ) +) + + +func NewApp(...) { + ... + + // use sdk StakingKeepeer + app.StakingKeeper = stakingkeeper.NewKeeper( + appCodec, + keys[stakingtypes.StoreKey], + app.AccountKeeper, + app.BankKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + app.MintKeeper = mintkeeper.NewKeeper( + appCodec, + keys[minttypes.StoreKey], + app.StakingKeeper, + app.AccountKeeper, + app.BankKeeper, + authtypes.FeeCollectorName, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + // no changes required for the distribution keeper + app.DistrKeeper = distrkeeper.NewKeeper( + appCodec, + keys[distrtypes.StoreKey], + app.AccountKeeper, + app.BankKeeper, + app.StakingKeeper, // keep StakingKeeper! + authtypes.FeeCollectorName, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + ++ // pre-initialize ConsumerKeeper to satsfy ibckeeper.NewKeeper ++ app.ConsumerKeeper = consumerkeeper.NewNonZeroKeeper( ++ appCodec, ++ keys[consumertypes.StoreKey], ++ app.GetSubspace(consumertypes.ModuleName), ++ ) ++ ++ app.IBCKeeper = ibckeeper.NewKeeper( ++ appCodec, ++ keys[ibchost.StoreKey], ++ app.GetSubspace(ibchost.ModuleName), ++ &app.ConsumerKeeper, ++ app.UpgradeKeeper, ++ scopedIBCKeeper, ++ ) ++ ++ // Create CCV consumer and modules ++ app.ConsumerKeeper = consumerkeeper.NewKeeper( ++ appCodec, ++ keys[consumertypes.StoreKey], ++ app.GetSubspace(consumertypes.ModuleName), ++ scopedIBCConsumerKeeper, ++ app.IBCKeeper.ChannelKeeper, ++ &app.IBCKeeper.PortKeeper, ++ app.IBCKeeper.ConnectionKeeper, ++ app.IBCKeeper.ClientKeeper, ++ app.SlashingKeeper, ++ app.BankKeeper, ++ app.AccountKeeper, ++ &app.TransferKeeper, ++ app.IBCKeeper, ++ authtypes.FeeCollectorName, ++ ) ++ ++ // Setting the standalone staking keeper is only needed for standalone to consumer changeover chains ++ // New chains using the democracy/staking do not need to set this ++ app.ConsumerKeeper.SetStandaloneStakingKeeper(app.StakingKeeper) + + + + // change the slashing keeper dependency + app.SlashingKeeper = slashingkeeper.NewKeeper( + appCodec, + legacyAmino, + keys[slashingtypes.StoreKey], +- app.StakingKeeper, ++ &app.ConsumerKeeper, // ConsumerKeeper implements StakingKeeper interface + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + // register slashing module StakingHooks to the consumer keeper ++ app.ConsumerKeeper = *app.ConsumerKeeper.SetHooks(app.SlashingKeeper.Hooks()) ++ consumerModule := consumer.NewAppModule(app.ConsumerKeeper, app.GetSubspace(consumertypes.ModuleName)) + + // register the module with module manager + // replace the x/staking module + app.MM = module.NewManager( + ... +- sdkstaking.NewAppModule(appCodec, &app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), ++ ccvstaking.NewAppModule(appCodec, *app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), + ... + ) +} +``` + +## Governance + +The `x/ccv/democracy/governance` module extends the `x/governance` module with the functionality to filter proposals. + +:::tip +Consumer chains can limit in the types of governance proposals that can be executed on chain to avoid inadvertant changes to interchain security protocol that could affect security properties. +::: + +The module uses `AnteHandler` to limit the types of proposals that can be executed. + +### Integration + +Add new `AnteHandler` to your `app`. + +```go + +// app/ante/forbidden_proposals.go +package ante + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" +) + +type ForbiddenProposalsDecorator struct { + isLegacyProposalWhitelisted func(govv1beta1.Content) bool + isModuleWhiteList func(string) bool +} + +func NewForbiddenProposalsDecorator( + whiteListFn func(govv1beta1.Content) bool, + isModuleWhiteList func(string) bool, +) ForbiddenProposalsDecorator { + return ForbiddenProposalsDecorator{ + isLegacyProposalWhitelisted: whiteListFn, + isModuleWhiteList: isModuleWhiteList, + } +} + +func (decorator ForbiddenProposalsDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + currHeight := ctx.BlockHeight() + + for _, msg := range tx.GetMsgs() { + // if the message is MsgSubmitProposal, check if proposal is whitelisted + submitProposalMgs, ok := msg.(*govv1.MsgSubmitProposal) + if !ok { + continue + } + + messages := submitProposalMgs.GetMessages() + for _, message := range messages { + if sdkMsg, isLegacyProposal := message.GetCachedValue().(*govv1.MsgExecLegacyContent); isLegacyProposal { + // legacy gov proposal content + content, err := govv1.LegacyContentFromMessage(sdkMsg) + if err != nil { + return ctx, fmt.Errorf("tx contains invalid LegacyContent") + } + if !decorator.isLegacyProposalWhitelisted(content) { + return ctx, fmt.Errorf("tx contains unsupported proposal message types at height %d", currHeight) + } + continue + } + // not legacy gov proposal content and not whitelisted + if !decorator.isModuleWhiteList(message.TypeUrl) { + return ctx, fmt.Errorf("tx contains unsupported proposal message types at height %d", currHeight) + } + } + } + + return next(ctx, tx, simulate) +} + +func IsProposalWhitelisted(content v1beta1.Content) bool { + switch c := content.(type) { + case *proposal.ParameterChangeProposal: + return isLegacyParamChangeWhitelisted(c.Changes) + + default: + return false + } +} + +func isLegacyParamChangeWhitelisted(paramChanges []proposal.ParamChange) bool { + for _, paramChange := range paramChanges { + _, found := LegacyWhitelistedParams[legacyParamChangeKey{Subspace: paramChange.Subspace, Key: paramChange.Key}] + if !found { + return false + } + } + return true +} + +type legacyParamChangeKey struct { + Subspace, Key string +} + +// Legacy params can be whitelisted +var LegacyWhitelistedParams = map[legacyParamChangeKey]struct{}{ + {Subspace: ibctransfertypes.ModuleName, Key: "SendEnabled"}: {}, + {Subspace: ibctransfertypes.ModuleName, Key: "ReceiveEnabled"}: {}, +} + +// New proposal types can be whitelisted +var WhiteListModule = map[string]struct{}{ + "/cosmos.gov.v1.MsgUpdateParams": {}, + "/cosmos.bank.v1beta1.MsgUpdateParams": {}, + "/cosmos.staking.v1beta1.MsgUpdateParams": {}, + "/cosmos.distribution.v1beta1.MsgUpdateParams": {}, + "/cosmos.mint.v1beta1.MsgUpdateParams": {}, +} + +func IsModuleWhiteList(typeUrl string) bool { + _, found := WhiteListModule[typeUrl] + return found +} +``` + +Add the `AnteHandler` to the list of supported antehandlers: + +```diff +// app/ante_handler.go +package app + +import ( + ... + ++ democracyante "github.com/cosmos/interchain-security/v4/app/consumer-democracy/ante" ++ consumerante "github.com/cosmos/interchain-security/v4/app/consumer/ante" ++ icsconsumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" +) + +type HandlerOptions struct { + ante.HandlerOptions + + IBCKeeper *ibckeeper.Keeper ++ ConsumerKeeper ibcconsumerkeeper.Keeper +} + +func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { + .... + + anteDecorators := []sdk.AnteDecorator{ + ... ++ consumerante.NewMsgFilterDecorator(options.ConsumerKeeper), ++ consumerante.NewDisabledModulesDecorator("/cosmos.evidence", "/cosmos.slashing"), ++ democracyante.NewForbiddenProposalsDecorator(IsProposalWhitelisted, IsModuleWhiteList), + ... + } + + return sdk.ChainAnteDecorators(anteDecorators...), nil +} +``` + +Wire the module in `app.go`. + +```diff +// app/app.go +package app +import ( + ... + sdkgov "github.com/cosmos/cosmos-sdk/x/gov" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + ++ ccvgov "github.com/cosmos/interchain-security/v4/x/ccv/democracy/governance" +) + +var ( + + // use sdk governance module + ModuleBasics = module.NewBasicManager( + ... + sdkgov.NewAppModuleBasic( + []govclient.ProposalHandler{ + paramsclient.ProposalHandler, + upgradeclient.LegacyProposalHandler, + upgradeclient.LegacyCancelProposalHandler, + }, + ), + ) +) + +func NewApp(...) { + // retain sdk gov router and keeper registrations + sdkgovRouter := govv1beta1.NewRouter() + sdkgovRouter. + AddRoute(govtypes.RouterKey, govv1beta1.ProposalHandler). + AddRoute(paramproposal.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper)). + AddRoute(upgradetypes.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(&app.UpgradeKeeper)) + govConfig := govtypes.DefaultConfig() + + app.GovKeeper = *govkeeper.NewKeeper( + appCodec, + keys[govtypes.StoreKey], + app.AccountKeeper, + app.BankKeeper, + app.StakingKeeper, + app.MsgServiceRouter(), + govConfig, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + app.GovKeeper.SetLegacyRouter(sdkgovRouter) + + + // register the module with module manager + // replace the x/gov module + app.MM = module.NewManager( +- sdkgov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper, IsProposalWhitelisted, app.GetSubspace(govtypes.ModuleName), IsModuleWhiteList), ++ ccvgov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper, IsProposalWhitelisted, app.GetSubspace(govtypes.ModuleName), IsModuleWhiteList), + ... + ) +} +``` + +## Distribution + +:::tip +The `democracy/distribution` module allows the consumer chain to send rewards to the provider chain while retaining the `x/distribution` module for internal reward distribution to Governators and stakers. +::: + +### How it works + +First, a % of rewards to be distributed to the provider chain's validator set is calculated and sent to the provider chain. Only opted-in validators from the provider chain will receive the consumer rewards. + +Second, the remaining rewards get distributed to the consumer chain's Governators and their delegators. + +:::info +The % that is sent to the provider chain corresponds to `1 - ConsumerRedistributionFraction`. + +e.g. `ConsumerRedistributionFraction = "0.75"` + +means that the consumer chain retains 75% of the rewards, while 25% gets sent to the provider chain to be distributed as rewards to provider chain validators. +::: + +### Integration + +Change the wiring in `app.go` + +```diff +import ( + ... + distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + sdkdistr "github.com/cosmos/cosmos-sdk/x/distribution" + ++ ccvdistr "github.com/cosmos/interchain-security/v4/x/ccv/democracy/distribution" +) + +var ( + // replace sdk distribution AppModuleBasic + ModuleBasics = module.NewBasicManager( + auth.AppModuleBasic{}, + genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator), + bank.AppModuleBasic{}, + capability.AppModuleBasic{}, + ccvstaking.AppModuleBasic{}, // make sure you first swap the staking keeper + mint.AppModuleBasic{}, +- sdkdistr.AppModuleBasic{}, ++ ccvdistr.AppModuleBasic{}, + ) +) + +func NewApp(...) { + .... + + app.DistrKeeper = distrkeeper.NewKeeper( + appCodec, + keys[distrtypes.StoreKey], + app.AccountKeeper, + app.BankKeeper, + app.StakingKeeper, // connect to sdk StakingKeeper + consumertypes.ConsumerRedistributeName, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + // register with the module manager + app.MM = module.NewManager( + ... +- sdkdistr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, *app.StakingKeeper, authtypes.FeeCollectorName, app.GetSubspace(distrtypes.ModuleName)), + ++ ccvdistr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, *app.StakingKeeper, authtypes.FeeCollectorName, app.GetSubspace(distrtypes.ModuleName)), + ccvstaking.NewAppModule(appCodec, *app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), + ... + ) +} +``` diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index c949763429..e73d5e2a21 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -190,7 +190,7 @@ const config = { prism: { theme: lightCodeTheme, darkTheme: darkCodeTheme, - additionalLanguages: ["protobuf", "go-module"], // https://prismjs.com/#supported-languages + additionalLanguages: ["protobuf", "go-module", "diff", "go"], // https://prismjs.com/#supported-languages }, // algolia: { // appId: "QLS2QSP47E", From a4c7a82d4206812368f783aac808543d425200b1 Mon Sep 17 00:00:00 2001 From: Marius Poke Date: Fri, 24 May 2024 16:18:00 +0200 Subject: [PATCH 040/102] docs: bring v4.2.0 changelog to main (#1909) bring v4.2.0 changelog to main --- ...1732-assigning-already-assigned-key-fix.md | 0 ...um-power-in-topN-to-consumer-chain-list.md | 0 .../dependencies/1876-bump-comet.md | 0 .../dependencies/1876-bump-sdk.md | 0 ...ble-opt-in-chains-through-gov-proposals.md | 0 .../features/provider}/1809-pss.md | 0 .../provider/1830-introduce-power-shaping.md | 2 + ...um-power-in-topN-to-consumer-chain-list.md | 0 ...query-for-latest-consumer-validator-set.md | 0 ...ble-opt-in-chains-through-gov-proposals.md | 0 ...1732-assigning-already-assigned-key-fix.md | 0 .../state-breaking/provider}/1809-pss.md | 0 .../provider/1830-introduce-power-shaping.md | 2 + .changelog/v4.2.0/summary.md | 1 + CHANGELOG.md | 48 +++++++++++++++++++ 15 files changed, 53 insertions(+) rename .changelog/{unreleased => v4.2.0}/api-breaking/provider/1732-assigning-already-assigned-key-fix.md (100%) rename .changelog/{unreleased => v4.2.0}/api-breaking/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md (100%) rename .changelog/{unreleased => v4.2.0}/dependencies/1876-bump-comet.md (100%) rename .changelog/{unreleased => v4.2.0}/dependencies/1876-bump-sdk.md (100%) rename .changelog/{unreleased => v4.2.0}/features/provider/1587-enable-opt-in-chains-through-gov-proposals.md (100%) rename .changelog/{unreleased/features => v4.2.0/features/provider}/1809-pss.md (100%) create mode 100644 .changelog/v4.2.0/features/provider/1830-introduce-power-shaping.md rename .changelog/{unreleased => v4.2.0}/features/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md (100%) rename .changelog/{unreleased => v4.2.0}/features/provider/1867-add-query-for-latest-consumer-validator-set.md (100%) rename .changelog/{unreleased => v4.2.0}/state-breaking/provider/1587-enable-opt-in-chains-through-gov-proposals.md (100%) rename .changelog/{unreleased => v4.2.0}/state-breaking/provider/1732-assigning-already-assigned-key-fix.md (100%) rename .changelog/{unreleased/state-breaking => v4.2.0/state-breaking/provider}/1809-pss.md (100%) create mode 100644 .changelog/v4.2.0/state-breaking/provider/1830-introduce-power-shaping.md create mode 100644 .changelog/v4.2.0/summary.md diff --git a/.changelog/unreleased/api-breaking/provider/1732-assigning-already-assigned-key-fix.md b/.changelog/v4.2.0/api-breaking/provider/1732-assigning-already-assigned-key-fix.md similarity index 100% rename from .changelog/unreleased/api-breaking/provider/1732-assigning-already-assigned-key-fix.md rename to .changelog/v4.2.0/api-breaking/provider/1732-assigning-already-assigned-key-fix.md diff --git a/.changelog/unreleased/api-breaking/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md b/.changelog/v4.2.0/api-breaking/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md similarity index 100% rename from .changelog/unreleased/api-breaking/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md rename to .changelog/v4.2.0/api-breaking/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md diff --git a/.changelog/unreleased/dependencies/1876-bump-comet.md b/.changelog/v4.2.0/dependencies/1876-bump-comet.md similarity index 100% rename from .changelog/unreleased/dependencies/1876-bump-comet.md rename to .changelog/v4.2.0/dependencies/1876-bump-comet.md diff --git a/.changelog/unreleased/dependencies/1876-bump-sdk.md b/.changelog/v4.2.0/dependencies/1876-bump-sdk.md similarity index 100% rename from .changelog/unreleased/dependencies/1876-bump-sdk.md rename to .changelog/v4.2.0/dependencies/1876-bump-sdk.md diff --git a/.changelog/unreleased/features/provider/1587-enable-opt-in-chains-through-gov-proposals.md b/.changelog/v4.2.0/features/provider/1587-enable-opt-in-chains-through-gov-proposals.md similarity index 100% rename from .changelog/unreleased/features/provider/1587-enable-opt-in-chains-through-gov-proposals.md rename to .changelog/v4.2.0/features/provider/1587-enable-opt-in-chains-through-gov-proposals.md diff --git a/.changelog/unreleased/features/1809-pss.md b/.changelog/v4.2.0/features/provider/1809-pss.md similarity index 100% rename from .changelog/unreleased/features/1809-pss.md rename to .changelog/v4.2.0/features/provider/1809-pss.md diff --git a/.changelog/v4.2.0/features/provider/1830-introduce-power-shaping.md b/.changelog/v4.2.0/features/provider/1830-introduce-power-shaping.md new file mode 100644 index 0000000000..87cd4590b4 --- /dev/null +++ b/.changelog/v4.2.0/features/provider/1830-introduce-power-shaping.md @@ -0,0 +1,2 @@ +- Introduce power-shaping features for consumer chains. The features: (i) allow us to cap the total number of validators that can validate the consumer chain, (ii) set a cap on the maximum voting power (percentage-wise) a validator can have on a consumer chain, and (iii) introduce allowlist and denylists to restrict which validators are allowed or not to validate a consumer chain. + ([\#1830](https://github.com/cosmos/interchain-security/pull/1830)) \ No newline at end of file diff --git a/.changelog/unreleased/features/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md b/.changelog/v4.2.0/features/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md similarity index 100% rename from .changelog/unreleased/features/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md rename to .changelog/v4.2.0/features/provider/1863-add-minimum-power-in-topN-to-consumer-chain-list.md diff --git a/.changelog/unreleased/features/provider/1867-add-query-for-latest-consumer-validator-set.md b/.changelog/v4.2.0/features/provider/1867-add-query-for-latest-consumer-validator-set.md similarity index 100% rename from .changelog/unreleased/features/provider/1867-add-query-for-latest-consumer-validator-set.md rename to .changelog/v4.2.0/features/provider/1867-add-query-for-latest-consumer-validator-set.md diff --git a/.changelog/unreleased/state-breaking/provider/1587-enable-opt-in-chains-through-gov-proposals.md b/.changelog/v4.2.0/state-breaking/provider/1587-enable-opt-in-chains-through-gov-proposals.md similarity index 100% rename from .changelog/unreleased/state-breaking/provider/1587-enable-opt-in-chains-through-gov-proposals.md rename to .changelog/v4.2.0/state-breaking/provider/1587-enable-opt-in-chains-through-gov-proposals.md diff --git a/.changelog/unreleased/state-breaking/provider/1732-assigning-already-assigned-key-fix.md b/.changelog/v4.2.0/state-breaking/provider/1732-assigning-already-assigned-key-fix.md similarity index 100% rename from .changelog/unreleased/state-breaking/provider/1732-assigning-already-assigned-key-fix.md rename to .changelog/v4.2.0/state-breaking/provider/1732-assigning-already-assigned-key-fix.md diff --git a/.changelog/unreleased/state-breaking/1809-pss.md b/.changelog/v4.2.0/state-breaking/provider/1809-pss.md similarity index 100% rename from .changelog/unreleased/state-breaking/1809-pss.md rename to .changelog/v4.2.0/state-breaking/provider/1809-pss.md diff --git a/.changelog/v4.2.0/state-breaking/provider/1830-introduce-power-shaping.md b/.changelog/v4.2.0/state-breaking/provider/1830-introduce-power-shaping.md new file mode 100644 index 0000000000..87cd4590b4 --- /dev/null +++ b/.changelog/v4.2.0/state-breaking/provider/1830-introduce-power-shaping.md @@ -0,0 +1,2 @@ +- Introduce power-shaping features for consumer chains. The features: (i) allow us to cap the total number of validators that can validate the consumer chain, (ii) set a cap on the maximum voting power (percentage-wise) a validator can have on a consumer chain, and (iii) introduce allowlist and denylists to restrict which validators are allowed or not to validate a consumer chain. + ([\#1830](https://github.com/cosmos/interchain-security/pull/1830)) \ No newline at end of file diff --git a/.changelog/v4.2.0/summary.md b/.changelog/v4.2.0/summary.md new file mode 100644 index 0000000000..d9bdda58b3 --- /dev/null +++ b/.changelog/v4.2.0/summary.md @@ -0,0 +1 @@ +May 17, 2024 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8629fe32df..9f8fd579ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,53 @@ # CHANGELOG +## v4.2.0 + +May 17, 2024 + +### API BREAKING + +- [Provider](x/ccv/provider) + - Assigning a key that is already assigned by the same validator will now be a no-op instead of throwing an error. + ([\#1732](https://github.com/cosmos/interchain-security/pull/1732)) + - Changes the `list-consumer-chains` query to include a `min_power_in_top_N` field, as well as fields for all power shaping parameters of the consumer. + ([\#1863](https://github.com/cosmos/interchain-security/pull/1863)) + +### DEPENDENCIES + +- Bump [CometBFT](https://github.com/cometbft/cometbft) to + [v0.37.6](https://github.com/cometbft/cometbft/releases/tag/v0.37.6). + ([\#1876](https://github.com/cosmos/interchain-security/pull/1876)) +- Bump [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) to + [v0.47.11](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.47.11). + ([\#1876](https://github.com/cosmos/interchain-security/pull/1876)) + +### FEATURES + +- [Provider](x/ccv/provider) + - Enable Opt In and Top N chains through gov proposals. + ([\#1587](https://github.com/cosmos/interchain-security/pull/1587)) + - Adding the Partial Set Security (PSS) feature cf. [ADR 015](https://cosmos.github.io/interchain-security/adrs/adr-015-partial-set-security). + PSS enables consumer chains to join ICS as _Top N_ or _Opt In_ chains and enables validators to opt to validate the consumer chains they want. + ([\#1809](https://github.com/cosmos/interchain-security/pull/1809)) + - Introduce power-shaping features for consumer chains. The features: (i) allow us to cap the total number of validators that can validate the consumer chain, (ii) set a cap on the maximum voting power (percentage-wise) a validator can have on a consumer chain, and (iii) introduce allowlist and denylists to restrict which validators are allowed or not to validate a consumer chain. + ([\#1830](https://github.com/cosmos/interchain-security/pull/1830)) + - Changes the `list-consumer-chains` query to include a `min_power_in_top_N` field, as well as fields for all power shaping parameters of the consumer. + ([\#1863](https://github.com/cosmos/interchain-security/pull/1863)) + - Introduces the `consumer-validators` query to retrieve the latest set consumer-validator set for a consumer chain. + ([\#1863](https://github.com/cosmos/interchain-security/pull/1867)) + +### STATE BREAKING + +- [Provider](x/ccv/provider) + - Enable Opt In and Top N chains through gov proposals. + ([\#1587](https://github.com/cosmos/interchain-security/pull/1587)) + - Assigning a key that is already assigned by the same validator will now be a no-op instead of throwing an error. + ([\#1732](https://github.com/cosmos/interchain-security/pull/1732)) + - Adding the Partial Set Security feature cf. [ADR 015](https://cosmos.github.io/interchain-security/adrs/adr-015-partial-set-security). + ([\#1809](https://github.com/cosmos/interchain-security/pull/1809)) + - Introduce power-shaping features for consumer chains. The features: (i) allow us to cap the total number of validators that can validate the consumer chain, (ii) set a cap on the maximum voting power (percentage-wise) a validator can have on a consumer chain, and (iii) introduce allowlist and denylists to restrict which validators are allowed or not to validate a consumer chain. + ([\#1830](https://github.com/cosmos/interchain-security/pull/1830)) + ## v4.1.1 *April 22, 2024* From 2960c5b2ea8089bb19290fbf2225a31339fbf601 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 16:18:24 +0200 Subject: [PATCH 041/102] build(deps): bump github.com/cosmos/ibc-go/v7 from 7.4.0 to 7.5.0 (#1907) * build(deps): bump github.com/cosmos/ibc-go/v7 from 7.4.0 to 7.5.0 Bumps [github.com/cosmos/ibc-go/v7](https://github.com/cosmos/ibc-go) from 7.4.0 to 7.5.0. - [Release notes](https://github.com/cosmos/ibc-go/releases) - [Changelog](https://github.com/cosmos/ibc-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/cosmos/ibc-go/compare/v7.4.0...v7.5.0) --- updated-dependencies: - dependency-name: github.com/cosmos/ibc-go/v7 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * add changelong entry --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: mpoke --- .changelog/unreleased/dependencies/1907-bump-ibc.md | 3 +++ go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 .changelog/unreleased/dependencies/1907-bump-ibc.md diff --git a/.changelog/unreleased/dependencies/1907-bump-ibc.md b/.changelog/unreleased/dependencies/1907-bump-ibc.md new file mode 100644 index 0000000000..73cb62b07b --- /dev/null +++ b/.changelog/unreleased/dependencies/1907-bump-ibc.md @@ -0,0 +1,3 @@ +- Bump [ibc-go](https://github.com/cosmos/ibc-go) to + [v7.5.0](https://github.com/cosmos/ibc-go/releases/tag/v7.5.0). + ([\#1907](https://github.com/cosmos/interchain-security/pull/1907)) \ No newline at end of file diff --git a/go.mod b/go.mod index 82b65dd7f6..68ff87c786 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/cometbft/cometbft-db v0.11.0 github.com/cosmos/cosmos-sdk v0.47.11 github.com/cosmos/gogoproto v1.4.10 - github.com/cosmos/ibc-go/v7 v7.4.0 + github.com/cosmos/ibc-go/v7 v7.5.0 github.com/cosmos/ics23/go v0.10.0 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.4 diff --git a/go.sum b/go.sum index 7c9ecfc0d9..6164e49fd2 100644 --- a/go.sum +++ b/go.sum @@ -347,8 +347,8 @@ github.com/cosmos/gogoproto v1.4.10 h1:QH/yT8X+c0F4ZDacDv3z+xE3WU1P1Z3wQoLMBRJoK github.com/cosmos/gogoproto v1.4.10/go.mod h1:3aAZzeRWpAwr+SS/LLkICX2/kDFyaYVzckBDzygIxek= github.com/cosmos/iavl v0.20.1 h1:rM1kqeG3/HBT85vsZdoSNsehciqUQPWrR4BYmqE2+zg= github.com/cosmos/iavl v0.20.1/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= -github.com/cosmos/ibc-go/v7 v7.4.0 h1:8FqYMptvksgMvlbN4UW9jFxTXzsPyfAzEZurujXac8M= -github.com/cosmos/ibc-go/v7 v7.4.0/go.mod h1:L/KaEhzV5TGUCTfGysVgMBQtl5Dm7hHitfpk+GIeoAo= +github.com/cosmos/ibc-go/v7 v7.5.0 h1:tvPyuTsNqS1hZK69Wq7MZIvZIg8AblMkcnkAndtQet0= +github.com/cosmos/ibc-go/v7 v7.5.0/go.mod h1:ktFg5GvKOyrGCqTWtW7Grj5uweU4ZapxrNeVS1CLLbo= 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.12.4 h1:drvWt+GJP7Aiw550yeb3ON/zsrgW0jgh5saFCr7pDnw= From 16ee6a22615f6bfcbe9778f904a90ba5dc855b1f Mon Sep 17 00:00:00 2001 From: Cosmos SDK <113218068+github-prbot@users.noreply.github.com> Date: Mon, 27 May 2024 15:12:45 +0200 Subject: [PATCH 042/102] chore: fix spelling errors (#1922) chore: spelling errors fixes Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com> --- docs/docs/features/democracy-modules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/features/democracy-modules.md b/docs/docs/features/democracy-modules.md index a138bb0413..5add40c67e 100644 --- a/docs/docs/features/democracy-modules.md +++ b/docs/docs/features/democracy-modules.md @@ -229,7 +229,7 @@ func NewApp(...) { The `x/ccv/democracy/governance` module extends the `x/governance` module with the functionality to filter proposals. :::tip -Consumer chains can limit in the types of governance proposals that can be executed on chain to avoid inadvertant changes to interchain security protocol that could affect security properties. +Consumer chains can limit in the types of governance proposals that can be executed on chain to avoid inadvertent changes to interchain security protocol that could affect security properties. ::: The module uses `AnteHandler` to limit the types of proposals that can be executed. From 3132f64ae49be052475be0c87ebe9c20ef939f1f Mon Sep 17 00:00:00 2001 From: Chris Ricketts <6156768+chris-ricketts@users.noreply.github.com> Date: Mon, 27 May 2024 15:08:55 +0100 Subject: [PATCH 043/102] fix(client): write unbonding period advisory to stderr instead of stdout (#1921) * fix(client): write unbonding period advisory to stderr instead of stdout * Add changelog for unbonding period advisory --------- Co-authored-by: Philip Offtermatt --- .changelog/unreleased/bug-fixes/1921-write-stderr.md | 2 ++ x/ccv/provider/client/proposal_handler.go | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 .changelog/unreleased/bug-fixes/1921-write-stderr.md diff --git a/.changelog/unreleased/bug-fixes/1921-write-stderr.md b/.changelog/unreleased/bug-fixes/1921-write-stderr.md new file mode 100644 index 0000000000..665ef78024 --- /dev/null +++ b/.changelog/unreleased/bug-fixes/1921-write-stderr.md @@ -0,0 +1,2 @@ +- Write unbonding period advisory to stderr instead of stdout + ([\#1921](https://github.com/cosmos/interchain-security/pull/1921)) \ No newline at end of file diff --git a/x/ccv/provider/client/proposal_handler.go b/x/ccv/provider/client/proposal_handler.go index bb88276393..022d131e4d 100644 --- a/x/ccv/provider/client/proposal_handler.go +++ b/x/ccv/provider/client/proposal_handler.go @@ -363,7 +363,8 @@ func CheckPropUnbondingPeriod(clientCtx client.Context, propUnbondingPeriod time providerUnbondingTime := res.Params.UnbondingTime if providerUnbondingTime < propUnbondingPeriod { - fmt.Printf( + fmt.Fprintf( + os.Stderr, `consumer unbonding period is advised to be smaller than provider unbonding period, but is longer. This is not a security risk, but will effectively lengthen the unbonding period on the provider. consumer unbonding: %s From 98bd90f63b77d2c5571af11f14786064d77bf555 Mon Sep 17 00:00:00 2001 From: Marius Poke Date: Mon, 27 May 2024 16:15:44 +0200 Subject: [PATCH 044/102] docs: update ADR metadata (#1910) * update ADR metadata * fix broken link --- .../adrs/adr-005-cryptographic-equivocation-verification.md | 2 +- docs/docs/adrs/adr-014-epochs.md | 2 +- docs/docs/adrs/adr-015-partial-set-security.md | 2 +- docs/docs/adrs/adr-016-securityaggregation.md | 6 +++++- docs/docs/adrs/intro.md | 5 +++-- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/docs/adrs/adr-005-cryptographic-equivocation-verification.md b/docs/docs/adrs/adr-005-cryptographic-equivocation-verification.md index 4438b83239..1e1a3f08cf 100644 --- a/docs/docs/adrs/adr-005-cryptographic-equivocation-verification.md +++ b/docs/docs/adrs/adr-005-cryptographic-equivocation-verification.md @@ -31,7 +31,7 @@ multiple nodes, called primary and witness nodes. Light clients download new headers committed on chain from a primary. Headers can be verified in two ways: sequentially, where the block height of headers is serial, or using skipping. This second verification method allows light clients to download headers -with nonconsecutive block height, where some intermediate headers are skipped (see [Tendermint Light Client, Figure 1 and Figure 3](https://arxiv.org/pdf/2010.07031.pdf)). +with nonconsecutive block height, where some intermediate headers are skipped (see [Tendermint Light Client, Figure 1 and Figure 3](https://arxiv.org/pdf/2010.07031)). Additionally, light clients are cross-checking new headers obtained from a primary with witnesses to ensure all nodes share the same state. A light client attack occurs when a Byzantine validator sends invalid headers to a light client. diff --git a/docs/docs/adrs/adr-014-epochs.md b/docs/docs/adrs/adr-014-epochs.md index fc669e9b36..7637078cf0 100644 --- a/docs/docs/adrs/adr-014-epochs.md +++ b/docs/docs/adrs/adr-014-epochs.md @@ -10,7 +10,7 @@ title: Epochs ## Status -Proposed +Accepted ## Context diff --git a/docs/docs/adrs/adr-015-partial-set-security.md b/docs/docs/adrs/adr-015-partial-set-security.md index e89c46b5cf..0c3a331dfb 100644 --- a/docs/docs/adrs/adr-015-partial-set-security.md +++ b/docs/docs/adrs/adr-015-partial-set-security.md @@ -10,7 +10,7 @@ title: Partial Set Security ## Status -Proposed +Accepted ## Context diff --git a/docs/docs/adrs/adr-016-securityaggregation.md b/docs/docs/adrs/adr-016-securityaggregation.md index 9ff4134854..0709f4a582 100644 --- a/docs/docs/adrs/adr-016-securityaggregation.md +++ b/docs/docs/adrs/adr-016-securityaggregation.md @@ -1,3 +1,7 @@ +--- +sidebar_position: 17 +title: Security aggregation +--- # ADR 016: Security aggregation ## Changelog @@ -6,7 +10,7 @@ ## Status -Draft +Proposed ## Context diff --git a/docs/docs/adrs/intro.md b/docs/docs/adrs/intro.md index 0ba2b5096e..c1e7c142b4 100644 --- a/docs/docs/adrs/intro.md +++ b/docs/docs/adrs/intro.md @@ -40,12 +40,13 @@ To suggest an ADR, please make use of the [ADR template](./adr-template.md) prov - [ADR 009: Soft Opt-Out](./adr-009-soft-opt-out.md) - [ADR 010: Standalone to Consumer Changeover](./adr-010-standalone-changeover.md) - [ADR 013: Slashing on the provider for consumer equivocation](./adr-013-equivocation-slashing.md) +- [ADR 014: Epochs](./adr-014-epochs.md) +- [ADR 015: Partial Set Security](./adr-015-partial-set-security.md) ### Proposed - [ADR 011: Improving testing and increasing confidence](./adr-011-improving-test-confidence.md) -- [ADR 014: Epochs](./adr-014-epochs.md) -- [ADR 015: Partial Set Security](./adr-015-partial-set-security.md) +- [ADR 016: Security aggregation](./adr-016-securityaggregation.md) ### Rejected From b9a889bf900cae495a60cf6199d7a8f48a6481bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 08:14:09 +0200 Subject: [PATCH 045/102] build(deps): bump github.com/cosmos/ibc-go/v7 from 7.5.0 to 7.5.1 (#1924) * build(deps): bump github.com/cosmos/ibc-go/v7 from 7.5.0 to 7.5.1 Bumps [github.com/cosmos/ibc-go/v7](https://github.com/cosmos/ibc-go) from 7.5.0 to 7.5.1. - [Release notes](https://github.com/cosmos/ibc-go/releases) - [Changelog](https://github.com/cosmos/ibc-go/blob/v7.5.1/CHANGELOG.md) - [Commits](https://github.com/cosmos/ibc-go/compare/v7.5.0...v7.5.1) --- updated-dependencies: - dependency-name: github.com/cosmos/ibc-go/v7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * update changelog entry --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: mpoke --- .changelog/unreleased/dependencies/1907-bump-ibc.md | 3 --- .changelog/unreleased/dependencies/1924-bump-ibc.md | 3 +++ go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 .changelog/unreleased/dependencies/1907-bump-ibc.md create mode 100644 .changelog/unreleased/dependencies/1924-bump-ibc.md diff --git a/.changelog/unreleased/dependencies/1907-bump-ibc.md b/.changelog/unreleased/dependencies/1907-bump-ibc.md deleted file mode 100644 index 73cb62b07b..0000000000 --- a/.changelog/unreleased/dependencies/1907-bump-ibc.md +++ /dev/null @@ -1,3 +0,0 @@ -- Bump [ibc-go](https://github.com/cosmos/ibc-go) to - [v7.5.0](https://github.com/cosmos/ibc-go/releases/tag/v7.5.0). - ([\#1907](https://github.com/cosmos/interchain-security/pull/1907)) \ No newline at end of file diff --git a/.changelog/unreleased/dependencies/1924-bump-ibc.md b/.changelog/unreleased/dependencies/1924-bump-ibc.md new file mode 100644 index 0000000000..8980dd3b9a --- /dev/null +++ b/.changelog/unreleased/dependencies/1924-bump-ibc.md @@ -0,0 +1,3 @@ +- Bump [ibc-go](https://github.com/cosmos/ibc-go) to + [v7.5.1](https://github.com/cosmos/ibc-go/releases/tag/v7.5.1). + ([\#1924](https://github.com/cosmos/interchain-security/pull/1924)) \ No newline at end of file diff --git a/go.mod b/go.mod index 68ff87c786..a8dbb8f182 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/cometbft/cometbft-db v0.11.0 github.com/cosmos/cosmos-sdk v0.47.11 github.com/cosmos/gogoproto v1.4.10 - github.com/cosmos/ibc-go/v7 v7.5.0 + github.com/cosmos/ibc-go/v7 v7.5.1 github.com/cosmos/ics23/go v0.10.0 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.4 diff --git a/go.sum b/go.sum index 6164e49fd2..7078201c92 100644 --- a/go.sum +++ b/go.sum @@ -347,8 +347,8 @@ github.com/cosmos/gogoproto v1.4.10 h1:QH/yT8X+c0F4ZDacDv3z+xE3WU1P1Z3wQoLMBRJoK github.com/cosmos/gogoproto v1.4.10/go.mod h1:3aAZzeRWpAwr+SS/LLkICX2/kDFyaYVzckBDzygIxek= github.com/cosmos/iavl v0.20.1 h1:rM1kqeG3/HBT85vsZdoSNsehciqUQPWrR4BYmqE2+zg= github.com/cosmos/iavl v0.20.1/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= -github.com/cosmos/ibc-go/v7 v7.5.0 h1:tvPyuTsNqS1hZK69Wq7MZIvZIg8AblMkcnkAndtQet0= -github.com/cosmos/ibc-go/v7 v7.5.0/go.mod h1:ktFg5GvKOyrGCqTWtW7Grj5uweU4ZapxrNeVS1CLLbo= +github.com/cosmos/ibc-go/v7 v7.5.1 h1:KqS/g7W7EMX1OtOvufS8lWMJibOKpdgtNNZIU6fAgVU= +github.com/cosmos/ibc-go/v7 v7.5.1/go.mod h1:ktFg5GvKOyrGCqTWtW7Grj5uweU4ZapxrNeVS1CLLbo= 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.12.4 h1:drvWt+GJP7Aiw550yeb3ON/zsrgW0jgh5saFCr7pDnw= From 8d0450a3d1fb644fa45387e923d1fda305c3cd44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 08:14:30 +0200 Subject: [PATCH 046/102] build(deps): bump bufbuild/buf-setup-action from 1.32.0 to 1.32.1 (#1923) Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.32.0 to 1.32.1. - [Release notes](https://github.com/bufbuild/buf-setup-action/releases) - [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.32.0...v1.32.1) --- updated-dependencies: - dependency-name: bufbuild/buf-setup-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/proto-registry.yml | 2 +- .github/workflows/proto.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/proto-registry.yml b/.github/workflows/proto-registry.yml index ee9b317a22..354f3d6cdb 100644 --- a/.github/workflows/proto-registry.yml +++ b/.github/workflows/proto-registry.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: bufbuild/buf-setup-action@v1.32.0 + - uses: bufbuild/buf-setup-action@v1.32.1 - uses: bufbuild/buf-push-action@v1 with: input: "proto" diff --git a/.github/workflows/proto.yml b/.github/workflows/proto.yml index 646655689f..930dd92830 100644 --- a/.github/workflows/proto.yml +++ b/.github/workflows/proto.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: bufbuild/buf-setup-action@v1.32.0 + - uses: bufbuild/buf-setup-action@v1.32.1 - uses: bufbuild/buf-breaking-action@v1 with: input: "proto" From b7d37faba88f02b14c98c771887fbdede6119c0e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:34:06 +0200 Subject: [PATCH 047/102] build(deps): bump bufbuild/buf-setup-action from 1.32.1 to 1.32.2 (#1934) Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.32.1 to 1.32.2. - [Release notes](https://github.com/bufbuild/buf-setup-action/releases) - [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.32.1...v1.32.2) --- updated-dependencies: - dependency-name: bufbuild/buf-setup-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/proto-registry.yml | 2 +- .github/workflows/proto.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/proto-registry.yml b/.github/workflows/proto-registry.yml index 354f3d6cdb..0dd39a112c 100644 --- a/.github/workflows/proto-registry.yml +++ b/.github/workflows/proto-registry.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: bufbuild/buf-setup-action@v1.32.1 + - uses: bufbuild/buf-setup-action@v1.32.2 - uses: bufbuild/buf-push-action@v1 with: input: "proto" diff --git a/.github/workflows/proto.yml b/.github/workflows/proto.yml index 930dd92830..68f31f2b67 100644 --- a/.github/workflows/proto.yml +++ b/.github/workflows/proto.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: bufbuild/buf-setup-action@v1.32.1 + - uses: bufbuild/buf-setup-action@v1.32.2 - uses: bufbuild/buf-breaking-action@v1 with: input: "proto" From 2eb04b0d0608ca6faee065b74f368cbcc13c5652 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:34:23 +0200 Subject: [PATCH 048/102] build(deps): bump github.com/spf13/viper from 1.18.2 to 1.19.0 (#1936) Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.18.2 to 1.19.0. - [Release notes](https://github.com/spf13/viper/releases) - [Commits](https://github.com/spf13/viper/compare/v1.18.2...v1.19.0) --- updated-dependencies: - dependency-name: github.com/spf13/viper dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index a8dbb8f182..a725965de2 100644 --- a/go.mod +++ b/go.mod @@ -92,7 +92,7 @@ require ( github.com/google/orderedcode v0.0.1 // 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.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.3 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect @@ -113,7 +113,7 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect - github.com/klauspost/compress v1.17.0 // indirect + github.com/klauspost/compress v1.17.2 // indirect github.com/lib/pq v1.10.7 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/linxGnu/grocksdb v1.8.12 // indirect @@ -127,7 +127,7 @@ require ( github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mtibben/percent v0.2.1 // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -154,7 +154,7 @@ require ( golang.org/x/oauth2 v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect - google.golang.org/api v0.169.0 // indirect + google.golang.org/api v0.171.0 // indirect google.golang.org/appengine v1.6.8 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect @@ -165,7 +165,7 @@ require ( require ( github.com/informalsystems/itf-go v0.0.1 - github.com/spf13/viper v1.18.2 + github.com/spf13/viper v1.19.0 golang.org/x/mod v0.17.0 google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 ) diff --git a/go.sum b/go.sum index 7078201c92..e2ef9f9286 100644 --- a/go.sum +++ b/go.sum @@ -595,8 +595,8 @@ github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99 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.2 h1:mhN09QQW1jEWeMF74zGR81R30z4VJzjZsfkUhuHF+DA= -github.com/googleapis/gax-go/v2 v2.12.2/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= +github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= +github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= 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= @@ -712,8 +712,8 @@ github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 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= @@ -840,8 +840,8 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= -github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +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-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU= @@ -953,8 +953,8 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= -github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +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= @@ -1445,8 +1445,8 @@ google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ 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.169.0 h1:QwWPy71FgMWqJN/l6jVlFHUa29a7dcUy02I8o799nPY= -google.golang.org/api v0.169.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg= +google.golang.org/api v0.171.0 h1:w174hnBPqut76FzW5Qaupt7zY8Kql6fiVjgys4f58sU= +google.golang.org/api v0.171.0/go.mod h1:Hnq5AHm4OTMt2BUVjael2CWZFD6vksJdWCWiUAmjC9o= 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= From 7bde2e8252f8150b5fd0279e9c63ec3f36fb14f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:34:35 +0200 Subject: [PATCH 049/102] build(deps): bump docker/login-action from 3.1.0 to 3.2.0 (#1935) Bumps [docker/login-action](https://github.com/docker/login-action) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/e92390c5fb421da1463c202d546fed0ec5c39f20...0d4c9c5ea7693da7b068278f7b52bda2a190a446) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 895ad0e4e3..bb9c86a3f9 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -57,7 +57,7 @@ jobs: # https://github.com/docker/login-action - name: Log into registry ${{ env.REGISTRY }} if: github.event_name != 'pull_request' - uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0 + uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} From 7e7fa43aa9aa3de95a5d9797bb4b9abad6601cfc Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Wed, 5 Jun 2024 12:30:14 +0200 Subject: [PATCH 050/102] docs: Add draft ADR for validators outside of the active set (#1879) * Add draft ADR for active set validators * Remove unused changelog entry * Add initial date and remove square brackets * Suggest alternative approach: unbonded validators can validate * Expand unbonded validators section a bit * Incorporate comments * Update docs/docs/adrs/adr-017-allowing-inactive-validators.md Co-authored-by: Marius Poke * Update docs/docs/adrs/adr-017-allowing-inactive-validators.md Co-authored-by: Marius Poke * Update docs/docs/adrs/adr-017-allowing-inactive-validators.md Co-authored-by: Marius Poke * Clarify consensus validators vs staking validators * Update ADR to roll context doc into it * Fix image links * Add negative consequence * Add sentence about module wiring * Address review comments * Add source code for diagrams and make text more technical * Update docs/docs/adrs/adr-017-allowing-inactive-validators.md Co-authored-by: Marius Poke * Update docs/docs/adrs/adr-017-allowing-inactive-validators.md Co-authored-by: Marius Poke * Update docs/docs/adrs/adr-017-allowing-inactive-validators.md Co-authored-by: Jehan * Write changes to state * Update intro, links, mitigations --------- Co-authored-by: Marius Poke Co-authored-by: Jehan --- .../adr-017-allowing-inactive-validators.md | 122 +++ docs/docs/adrs/intro.md | 1 + docs/figures/inactivevals_after.excalidraw | 847 ++++++++++++++++++ docs/figures/inactivevals_after.png | Bin 0 -> 78614 bytes docs/figures/inactivevals_before.excalidraw | 659 ++++++++++++++ docs/figures/inactivevals_before.png | Bin 0 -> 65423 bytes 6 files changed, 1629 insertions(+) create mode 100644 docs/docs/adrs/adr-017-allowing-inactive-validators.md create mode 100644 docs/figures/inactivevals_after.excalidraw create mode 100644 docs/figures/inactivevals_after.png create mode 100644 docs/figures/inactivevals_before.excalidraw create mode 100644 docs/figures/inactivevals_before.png diff --git a/docs/docs/adrs/adr-017-allowing-inactive-validators.md b/docs/docs/adrs/adr-017-allowing-inactive-validators.md new file mode 100644 index 0000000000..9a3715f135 --- /dev/null +++ b/docs/docs/adrs/adr-017-allowing-inactive-validators.md @@ -0,0 +1,122 @@ +--- +sidebar_position: 18 +title: ICS with Inactive Provider Validators +--- +# ADR 017: ICS with Inactive Provider Validators + +## Changelog +* 15th May 2024: Initial draft + + +## Status + +Proposed + +## Context + +Currently, only validators in the active set on the provider can validate on consumer chains, which limits the number of validators that can participate in Interchain Security (ICS). +Validators outside of the active set might be willing +to validate on consumer chains, but we might not want to make the provider validator set larger, e.g. to not put more strain on the consensus engine. +This runs the risk of leaving consumer chains with too few validators. + +The purpose of this ADR is to allow validators that are *not* part of the consensus process on the provider chain (because they are inactive) +to validate on consumer chains. + +In the context of this ADR, "consensus validator set" is the set of validators participating in the consensus protocol, and "staking validator set" is the set of validators viewed as active by the staking module. + +Currently, the staking module, provider module, and CometBFT interact in this way: + +![inactivevals_before.png](../../figures/inactivevals_before.png) + +The staking module keeps a list of validators. The `MaxValidators` validators with the largest amount of stake are "active" validators. `MaxValidators` is a parameter of the staking module. The staking module sends these validators to CometBFT to inform which validators make up the next consensus validators, that is, the set of validators participating in the consensus process. Separately, the provider module reads the list of bonded validators and sends this to the consumer chain, after shaping it according to which validators are opted in and the parameters set by the consumer chain for allowlist, denylist, etc. + +## Decision + +The proposed solution to allow validators that are not participating in the consensus process on the provider (inactive validators) is to change 3 main things: + +a) increase the `MaxValidators` parameter of the staking module + +b) do *not* take the updates for CometBFT directly from the bonded validators in the staking module, by wrapping the staking modules `EndBlocker` with a dummy EndBlocker that doesn't return any validator updates. Instead, we adjust the provider module to return validator updates on its EndBlocker. These validator updates are obtained by *filtering* the bonded validators to send only the first `MaxProviderConsensusValidators` (sorted by largest amount of stake first) many validators to CometBFT + +c) use the enlarged list of bonded validators from the staking module as basis for the validator set that the provider module sends to consumer chains (again after applying power shaping and filtering out validatiors that are not opted in). + +In consequence, the provider chain can keep a reasonably-sized consensus validator set, while giving consumer chains a much larger pool of potential validators. + +![inactivevals_after.png](../../figures/inactivevals_after.png) + + +Some additional considerations: + +* Migration: In the migration, the last consensus validator set will be set to the last active validator set from the view of the staking module. Existing consumer chains are migrated to have a validator set size cap (otherwise, they could end up with a huge validator set including all the staking-but-not-consensus-active validators from the provider chain) +* Slashing: Validators that are not part of the active set on the provider chain can still be jailed for downtime on a consumer chain (via an Interchain Security SlashPacket sent to the provider, who will then jail the validator), but they *are not* slashed for downtime on the provider chain. +This is achieved without any additional changes to the slashing module, because the slashing module checks for downtime by looking at the consensus participants reported by CometBFT, and thus with the proposed solution, validators that are not part of the consensus validators on the provider chain are not considered for downtime slashing (see https://github.com/cosmos/cosmos-sdk/blob/v0.47.11/x/slashing/abci.go#L22). +* Rewards: Validators that are not part of the active set on the provider chain can still receive rewards on the consumer chain, but they *do not* receive rewards from the provider chain. This change is +achieved without further changes to staking or reward distributions, because similar to downtime, rewards are based on the consensus validator set (see https://github.com/cosmos/cosmos-sdk/blob/v0.47.11/x/distribution/abci.go#L28) + + +### Changes to the state + +The following changes to the state are required: + +* Introduce the `MaxProviderConsensusValidators` parameter to the provider module, which is the number of validators that the provider module will send to consumer chains. +* Store the provider consensus validator set in the provider module state under the `LastProviderConsensusValsPrefix` key. This is the last set of validators that the provider sent to the consensus engine. This is needed to compute the ValUpdates to send to the consensus engine (by diffing the current set with this last sent set). +* Increase the `MaxValidators` parameter of the staking module to the desired size of the potential validator +set of consumer chains. + +## Risk Mitigations + +To mitigate risks from validators with little stake, we introduce a minimum stake requirement for validators to be able to validate on consumer chains, which can be set by each consumer chain independently, with a default value set by the provider chain. + +Additionally, we indepdently allow individual consumer chains to disable this feature, which will disallow validators from outside the provider active set from validating on the consumer chain and revert them to the previous behaviour of only considering validators of the provider that are part of the active consensus validator set. + +Additional risk mitigations are to increase the active set size slowly, and to monitor the effects on the network closely. For the first iteration, we propose to increase the active set size to 200 validators (while keeping the consensus validators to 180), thus letting the 20 validators with the most stake outside of the active set validate on consumer chains. + +## Consequences + +### Positive + +* Validators outside of the active set can validate on consumer chains without having an impact on the consensus engine of the provider chain +* Consumer chains can have a much larger validator set than the provider chain if they prefer this e.g. for decentralization reasons +* Consumer chain teams can, with much less cost than today, start up their own consumer chain node to keep the chain running (in a centralized manner) even if no hub validators have opted in to validate on the chain. This is useful to stop the chain from ending up with an empty validator set and becoming recoverable only with a hardfork + +### Negative + +Allowing validators from the inactive set brings with it some additional risks. +In general, consumer chains will now face some of the problems also faced by standalone chains. It’s reasonable to assume that the validator set on the hub has a minimum amount of operational quality due to being battle tested and decentralized, and consumer chains with validators from outside the hub active set cannot rely on this as much anymore. + + +#### Sybil attacks + +With the restricted size of the active set today, it’s clear that the set is at least minimally competitive and it is not trivial to spin up multiple nodes as a validator. + +When we make the “potential validator set” much larger, we should assume that it becomes much less competitive to be part of that set, and thus trivial for single entities to control many of those validators. + +#### Reputational damage is not a deterrent + +For validators in the active set, we typically assume that if they would misbehave, they pay a large reputational cost. This represents delegators deciding to switch validators (potentially even on chains other than the one the misbehaviour happened on), and loss of credibility in the ecosystem. With the much larger active set, it seems prudent to assume that reputational damage is not a deterrent for many validators. They might only have minimal amounts of delegated stake and control most of it themselves, so they might not be deterred from performing actions that would usually bring reputational damage. + +#### Additional negative consequences +* The provider keeper will need to implement the staking keeper interface, and modules need to be wired up to either the staking or provider keeper, depending on whether they need the consensus or staking validator set +* This will impact how future modules are integrated, since we will need to consider whether those modules should consider the consensus validators or the bonded validators (which other modules might assume to be the same) + +### Neutral + +* There might be validators that are bonded, but not validating on any chain at all. This is not a problem, but it might be a bit confusing. + +## Alternative considerations + +### Modifying the staking module + +We could instead adapt the *staking module* with a similar change. +This might be better if it turns out that the staking module active set is used in many other places. + +### Allowing unbonding validators to validate + +Instead of increasing the active set size, we could allow validators that are unbonded (but still exist on the provider) to validate consumer chains. +For this, we would need to: +* Modify the VSC updates to consider the set of all validators, even unbonded ones, instead of just active ones +* Adjust our downtime jailing/equivocation slashing logic to work correctly with unbonded validators. This is very hard, because redelegations are not usually tracked for unbonded validators. + +## References + +* [Security Aggregation](./adr-016-securityaggregation.md) has similar concerns where the staking validator set will differ from the consensus validator set diff --git a/docs/docs/adrs/intro.md b/docs/docs/adrs/intro.md index c1e7c142b4..fc7d06a152 100644 --- a/docs/docs/adrs/intro.md +++ b/docs/docs/adrs/intro.md @@ -47,6 +47,7 @@ To suggest an ADR, please make use of the [ADR template](./adr-template.md) prov - [ADR 011: Improving testing and increasing confidence](./adr-011-improving-test-confidence.md) - [ADR 016: Security aggregation](./adr-016-securityaggregation.md) +- [ADR 017: ICS with Inactive Provider Validators](./adr-017-allowing-inactive-validators.md) ### Rejected diff --git a/docs/figures/inactivevals_after.excalidraw b/docs/figures/inactivevals_after.excalidraw new file mode 100644 index 0000000000..9a618ddf8c --- /dev/null +++ b/docs/figures/inactivevals_after.excalidraw @@ -0,0 +1,847 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "nJJZVzll88P_dmpDsWkuC", + "type": "rectangle", + "x": 551, + "y": -843, + "width": 657, + "height": 655, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0", + "roundness": { + "type": 3 + }, + "seed": 951962170, + "version": 63, + "versionNonce": 1865032870, + "isDeleted": false, + "boundElements": null, + "updated": 1717506390574, + "link": null, + "locked": false + }, + { + "id": "fVs1LhHvrrvCK3ZhJRtu5", + "type": "text", + "x": 575, + "y": -891, + "width": 249.3000030517578, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a1", + "roundness": null, + "seed": 129610534, + "version": 46, + "versionNonce": 1276535974, + "isDeleted": false, + "boundElements": null, + "updated": 1717507131314, + "link": null, + "locked": false, + "text": "Provider Chain", + "fontSize": 36, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Provider Chain", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "_Dx7IQJkJzNifgDjIf2sI", + "type": "rectangle", + "x": 622, + "y": -764, + "width": 161, + "height": 281, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a2", + "roundness": { + "type": 3 + }, + "seed": 1049314490, + "version": 59, + "versionNonce": 1789968378, + "isDeleted": false, + "boundElements": [ + { + "id": "1QU4AFcgTvpCQQURDCxDZ", + "type": "arrow" + }, + { + "type": "text", + "id": "OyUuFa30MbHO4QszYQXEH" + } + ], + "updated": 1717508356543, + "link": null, + "locked": false + }, + { + "id": "OyUuFa30MbHO4QszYQXEH", + "type": "text", + "x": 667.3666648864746, + "y": -648.5, + "width": 70.26667022705078, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a2G", + "roundness": null, + "seed": 335756666, + "version": 18, + "versionNonce": 1919688890, + "isDeleted": false, + "boundElements": null, + "updated": 1717507131314, + "link": null, + "locked": false, + "text": "Staking\nModule", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "_Dx7IQJkJzNifgDjIf2sI", + "originalText": "Staking\nModule", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 230, + "versionNonce": 188847418, + "index": "a4", + "isDeleted": false, + "id": "wJsk4gejRqDCjtFbAeg8G", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 962.5, + "y": -756.5, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 186.99999999999997, + "height": 309, + "seed": 409738746, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "A8ifOchQWi_H__sEvTMnW", + "type": "arrow" + }, + { + "id": "XBn8pRQaZVQe5gai59xYl", + "type": "arrow" + } + ], + "updated": 1717507815940, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 351, + "versionNonce": 357216442, + "index": "a5", + "isDeleted": false, + "id": "n5HV8y4tI6DZYWWA1Koq6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 620.5, + "y": -376.5, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 164, + "height": 120.00000000000003, + "seed": 1248958374, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "TKqZQfAcUP-6Q6EoH8fp0" + }, + { + "id": "QVrMGeKQYVvkrmDV5wKWR", + "type": "arrow" + } + ], + "updated": 1717508356543, + "link": null, + "locked": false + }, + { + "id": "TKqZQfAcUP-6Q6EoH8fp0", + "type": "text", + "x": 652.0166664123535, + "y": -329, + "width": 100.96666717529297, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a6", + "roundness": null, + "seed": 438334118, + "version": 22, + "versionNonce": 2019293562, + "isDeleted": false, + "boundElements": null, + "updated": 1717507131314, + "link": null, + "locked": false, + "text": "CometBFT", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "n5HV8y4tI6DZYWWA1Koq6", + "originalText": "CometBFT", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "FPk9JragWk96YKRlFekY6", + "type": "rectangle", + "x": 1026, + "y": -678, + "width": 108.00000000000003, + "height": 98, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a8", + "roundness": { + "type": 3 + }, + "seed": 580928678, + "version": 174, + "versionNonce": 683525946, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "R6sc8M7CuGEvv7N7-fcTZ" + }, + { + "id": "1QU4AFcgTvpCQQURDCxDZ", + "type": "arrow" + }, + { + "id": "A8ifOchQWi_H__sEvTMnW", + "type": "arrow" + } + ], + "updated": 1717507761939, + "link": null, + "locked": false + }, + { + "id": "R6sc8M7CuGEvv7N7-fcTZ", + "type": "text", + "x": 1034.849998474121, + "y": -666.5, + "width": 90.30000305175781, + "height": 75, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a9", + "roundness": null, + "seed": 758773690, + "version": 153, + "versionNonce": 596469030, + "isDeleted": false, + "boundElements": null, + "updated": 1717507759332, + "link": null, + "locked": false, + "text": "Power \nShaping\n& Opt In", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "FPk9JragWk96YKRlFekY6", + "originalText": "Power Shaping\n& Opt In", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "1QU4AFcgTvpCQQURDCxDZ", + "type": "arrow", + "x": 784, + "y": -617.9538520826802, + "width": 240, + "height": 19.20103304326051, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aA", + "roundness": { + "type": 2 + }, + "seed": 75530874, + "version": 385, + "versionNonce": 598135718, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "teEeVARH2zRlZXGyzB2PV" + } + ], + "updated": 1717507759332, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 240, + -19.20103304326051 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "_Dx7IQJkJzNifgDjIf2sI", + "focus": 0.08197485638165014, + "gap": 1 + }, + "endBinding": { + "elementId": "FPk9JragWk96YKRlFekY6", + "focus": 0.23696682464454974, + "gap": 2 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "teEeVARH2zRlZXGyzB2PV", + "type": "text", + "x": 837.0583343505859, + "y": -639.5, + "width": 99.88333129882812, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aB", + "roundness": null, + "seed": 918244474, + "version": 27, + "versionNonce": 206748218, + "isDeleted": false, + "boundElements": null, + "updated": 1717507131314, + "link": null, + "locked": false, + "text": "Bonded\nValidators", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "1QU4AFcgTvpCQQURDCxDZ", + "originalText": "Bonded\nValidators", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "9Z_qOWKQyxwUvelhZMZx6", + "type": "text", + "x": 1011, + "y": -751, + "width": 78.66666412353516, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aE", + "roundness": null, + "seed": 91824166, + "version": 99, + "versionNonce": 207872422, + "isDeleted": false, + "boundElements": null, + "updated": 1717507131314, + "link": null, + "locked": false, + "text": "Provider\nModule", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Provider\nModule", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "A8ifOchQWi_H__sEvTMnW", + "type": "arrow", + "x": 1134, + "y": -632.7473377850915, + "width": 185, + "height": 10.747337785091531, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aF", + "roundness": { + "type": 2 + }, + "seed": 157350886, + "version": 351, + "versionNonce": 1955289658, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "IWrcqs7yN7FdMaMGcNDj4" + } + ], + "updated": 1717507763008, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 185, + 10.747337785091531 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "FPk9JragWk96YKRlFekY6", + "focus": -0.1320442396210268, + "gap": 1 + }, + "endBinding": { + "elementId": "zY_dSGPd9zunChgVkNDpR", + "focus": -0.020248008412827926, + "gap": 1.5583343505859375 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "IWrcqs7yN7FdMaMGcNDj4", + "type": "text", + "x": 1190.216667175293, + "y": -647, + "width": 67.56666564941406, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aG", + "roundness": null, + "seed": 1111293542, + "version": 19, + "versionNonce": 1166078906, + "isDeleted": false, + "boundElements": null, + "updated": 1717507131314, + "link": null, + "locked": false, + "text": "Shaped\nValSet", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "A8ifOchQWi_H__sEvTMnW", + "originalText": "Shaped\nValSet", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "zY_dSGPd9zunChgVkNDpR", + "type": "text", + "x": 1320.558334350586, + "y": -645, + "width": 84.88333129882812, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aH", + "roundness": null, + "seed": 359543462, + "version": 97, + "versionNonce": 589863290, + "isDeleted": false, + "boundElements": [ + { + "id": "A8ifOchQWi_H__sEvTMnW", + "type": "arrow" + } + ], + "updated": 1717507761940, + "link": null, + "locked": false, + "text": "sent to\nconsumer", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "sent to\nconsumer", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 259, + "versionNonce": 827676730, + "index": "aI", + "isDeleted": false, + "id": "kTEf4qbgYHvfyWHxD6yV2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 977, + "y": -555, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 152.00000000000006, + "height": 100, + "seed": 1312320038, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "TKt49fH0utbl-oDqZWAHv" + }, + { + "id": "XBn8pRQaZVQe5gai59xYl", + "type": "arrow" + }, + { + "id": "QVrMGeKQYVvkrmDV5wKWR", + "type": "arrow" + } + ], + "updated": 1717507832443, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 273, + "versionNonce": 640336678, + "index": "aJ", + "isDeleted": false, + "id": "TKt49fH0utbl-oDqZWAHv", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 987.3499984741211, + "y": -542.5, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 131.3000030517578, + "height": 75, + "seed": 489960806, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1717507777794, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Filter to\nsmall set of \nvalidators", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "kTEf4qbgYHvfyWHxD6yV2", + "originalText": "Filter to\nsmall set of validators", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "XBn8pRQaZVQe5gai59xYl", + "type": "arrow", + "x": 980, + "y": -633, + "width": 15, + "height": 78, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aK", + "roundness": { + "type": 2 + }, + "seed": 2081760186, + "version": 43, + "versionNonce": 557255654, + "isDeleted": false, + "boundElements": null, + "updated": 1717507819360, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 15, + 78 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "wJsk4gejRqDCjtFbAeg8G", + "focus": 0.5684407679100983, + "gap": 1 + }, + "endBinding": { + "elementId": "kTEf4qbgYHvfyWHxD6yV2", + "focus": -0.5651392632524705, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "QVrMGeKQYVvkrmDV5wKWR", + "type": "arrow", + "x": 981, + "y": -466, + "width": 196, + "height": 106, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aL", + "roundness": { + "type": 2 + }, + "seed": 390002426, + "version": 59, + "versionNonce": 832345722, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "Usz338HAejcFqJG1vmJ4x" + } + ], + "updated": 1717507912037, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -196, + 106 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "kTEf4qbgYHvfyWHxD6yV2", + "focus": -0.000672043010752688, + "gap": 1 + }, + "endBinding": { + "elementId": "n5HV8y4tI6DZYWWA1Koq6", + "focus": 0.01070799921768042, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "Usz338HAejcFqJG1vmJ4x", + "type": "text", + "x": 827.5499992370605, + "y": -425.5, + "width": 110.9000015258789, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aM", + "roundness": null, + "seed": 920641702, + "version": 12, + "versionNonce": 1488639910, + "isDeleted": false, + "boundElements": null, + "updated": 1717507911327, + "link": null, + "locked": false, + "text": "ValUpdates", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "QVrMGeKQYVvkrmDV5wKWR", + "originalText": "ValUpdates", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/figures/inactivevals_after.png b/docs/figures/inactivevals_after.png new file mode 100644 index 0000000000000000000000000000000000000000..fb9e862c025176a63c1d666add74edf6ec59d97f GIT binary patch literal 78614 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 literal 0 HcmV?d00001 diff --git a/docs/figures/inactivevals_before.excalidraw b/docs/figures/inactivevals_before.excalidraw new file mode 100644 index 0000000000..ea1d06352a --- /dev/null +++ b/docs/figures/inactivevals_before.excalidraw @@ -0,0 +1,659 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "nJJZVzll88P_dmpDsWkuC", + "type": "rectangle", + "x": 551, + "y": -843, + "width": 657, + "height": 655, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0", + "roundness": { + "type": 3 + }, + "seed": 951962170, + "version": 63, + "versionNonce": 1865032870, + "isDeleted": false, + "boundElements": null, + "updated": 1717506390574, + "link": null, + "locked": false + }, + { + "id": "fVs1LhHvrrvCK3ZhJRtu5", + "type": "text", + "x": 575, + "y": -891, + "width": 249.3000030517578, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a1", + "roundness": null, + "seed": 129610534, + "version": 46, + "versionNonce": 1276535974, + "isDeleted": false, + "boundElements": null, + "updated": 1717507131314, + "link": null, + "locked": false, + "text": "Provider Chain", + "fontSize": 36, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Provider Chain", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "_Dx7IQJkJzNifgDjIf2sI", + "type": "rectangle", + "x": 622, + "y": -764, + "width": 161, + "height": 281, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a2", + "roundness": { + "type": 3 + }, + "seed": 1049314490, + "version": 58, + "versionNonce": 250933030, + "isDeleted": false, + "boundElements": [ + { + "id": "SPm0CjdkwnTk8Wfol8gqy", + "type": "arrow" + }, + { + "id": "1QU4AFcgTvpCQQURDCxDZ", + "type": "arrow" + }, + { + "type": "text", + "id": "OyUuFa30MbHO4QszYQXEH" + } + ], + "updated": 1717506907807, + "link": null, + "locked": false + }, + { + "id": "OyUuFa30MbHO4QszYQXEH", + "type": "text", + "x": 667.3666648864746, + "y": -648.5, + "width": 70.26667022705078, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a2G", + "roundness": null, + "seed": 335756666, + "version": 18, + "versionNonce": 1919688890, + "isDeleted": false, + "boundElements": null, + "updated": 1717507131314, + "link": null, + "locked": false, + "text": "Staking\nModule", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "_Dx7IQJkJzNifgDjIf2sI", + "originalText": "Staking\nModule", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 133, + "versionNonce": 1967865830, + "index": "a4", + "isDeleted": false, + "id": "wJsk4gejRqDCjtFbAeg8G", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 966.5, + "y": -764.5, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 161, + "height": 281, + "seed": 409738746, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "A8ifOchQWi_H__sEvTMnW", + "type": "arrow" + } + ], + "updated": 1717507105162, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 349, + "versionNonce": 1873924346, + "index": "a5", + "isDeleted": false, + "id": "n5HV8y4tI6DZYWWA1Koq6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 620.5, + "y": -376.5, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 164, + "height": 120.00000000000003, + "seed": 1248958374, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "TKqZQfAcUP-6Q6EoH8fp0" + }, + { + "id": "SPm0CjdkwnTk8Wfol8gqy", + "type": "arrow" + } + ], + "updated": 1717506820805, + "link": null, + "locked": false + }, + { + "id": "TKqZQfAcUP-6Q6EoH8fp0", + "type": "text", + "x": 652.0166664123535, + "y": -329, + "width": 100.96666717529297, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a6", + "roundness": null, + "seed": 438334118, + "version": 22, + "versionNonce": 2019293562, + "isDeleted": false, + "boundElements": null, + "updated": 1717507131314, + "link": null, + "locked": false, + "text": "CometBFT", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "n5HV8y4tI6DZYWWA1Koq6", + "originalText": "CometBFT", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "SPm0CjdkwnTk8Wfol8gqy", + "type": "arrow", + "x": 697, + "y": -480, + "width": 0, + "height": 106, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a7", + "roundness": { + "type": 2 + }, + "seed": 906345786, + "version": 53, + "versionNonce": 648670650, + "isDeleted": false, + "boundElements": null, + "updated": 1717506825727, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 106 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "_Dx7IQJkJzNifgDjIf2sI", + "focus": 0.06832298136645963, + "gap": 3 + }, + "endBinding": { + "elementId": "n5HV8y4tI6DZYWWA1Koq6", + "focus": -0.06707317073170732, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "FPk9JragWk96YKRlFekY6", + "type": "rectangle", + "x": 995, + "y": -654, + "width": 108.00000000000003, + "height": 98, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a8", + "roundness": { + "type": 3 + }, + "seed": 580928678, + "version": 111, + "versionNonce": 1002770426, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "R6sc8M7CuGEvv7N7-fcTZ" + }, + { + "id": "1QU4AFcgTvpCQQURDCxDZ", + "type": "arrow" + } + ], + "updated": 1717506889152, + "link": null, + "locked": false + }, + { + "id": "R6sc8M7CuGEvv7N7-fcTZ", + "type": "text", + "x": 1003.8499984741211, + "y": -642.5, + "width": 90.30000305175781, + "height": 75, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a9", + "roundness": null, + "seed": 758773690, + "version": 91, + "versionNonce": 1184232230, + "isDeleted": false, + "boundElements": null, + "updated": 1717507131314, + "link": null, + "locked": false, + "text": "Power \nShaping\n& Opt In", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "FPk9JragWk96YKRlFekY6", + "originalText": "Power Shaping\n& Opt In", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "1QU4AFcgTvpCQQURDCxDZ", + "type": "arrow", + "x": 781, + "y": -613, + "width": 212, + "height": 3, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aA", + "roundness": { + "type": 2 + }, + "seed": 75530874, + "version": 55, + "versionNonce": 700245990, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "teEeVARH2zRlZXGyzB2PV" + } + ], + "updated": 1717506903639, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 212, + -3 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "_Dx7IQJkJzNifgDjIf2sI", + "focus": 0.08197485638165014, + "gap": 1 + }, + "endBinding": { + "elementId": "FPk9JragWk96YKRlFekY6", + "focus": 0.23696682464454974, + "gap": 2 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "teEeVARH2zRlZXGyzB2PV", + "type": "text", + "x": 837.0583343505859, + "y": -639.5, + "width": 99.88333129882812, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aB", + "roundness": null, + "seed": 918244474, + "version": 27, + "versionNonce": 206748218, + "isDeleted": false, + "boundElements": null, + "updated": 1717507131314, + "link": null, + "locked": false, + "text": "Bonded\nValidators", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "1QU4AFcgTvpCQQURDCxDZ", + "originalText": "Bonded\nValidators", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "9Z_qOWKQyxwUvelhZMZx6", + "type": "text", + "x": 1011, + "y": -751, + "width": 78.66666412353516, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aE", + "roundness": null, + "seed": 91824166, + "version": 99, + "versionNonce": 207872422, + "isDeleted": false, + "boundElements": null, + "updated": 1717507131314, + "link": null, + "locked": false, + "text": "Provider\nModule", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Provider\nModule", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "A8ifOchQWi_H__sEvTMnW", + "type": "arrow", + "x": 1129, + "y": -622, + "width": 190, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aF", + "roundness": { + "type": 2 + }, + "seed": 157350886, + "version": 83, + "versionNonce": 738572602, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "IWrcqs7yN7FdMaMGcNDj4" + } + ], + "updated": 1717507110279, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 190, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "wJsk4gejRqDCjtFbAeg8G", + "focus": 0.014234875444839857, + "gap": 1.5 + }, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "IWrcqs7yN7FdMaMGcNDj4", + "type": "text", + "x": 1190.216667175293, + "y": -647, + "width": 67.56666564941406, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aG", + "roundness": null, + "seed": 1111293542, + "version": 19, + "versionNonce": 1166078906, + "isDeleted": false, + "boundElements": null, + "updated": 1717507131314, + "link": null, + "locked": false, + "text": "Shaped\nValSet", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "A8ifOchQWi_H__sEvTMnW", + "originalText": "Shaped\nValSet", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "zY_dSGPd9zunChgVkNDpR", + "type": "text", + "x": 1320.558334350586, + "y": -645, + "width": 84.88333129882812, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aH", + "roundness": null, + "seed": 359543462, + "version": 96, + "versionNonce": 100973798, + "isDeleted": false, + "boundElements": null, + "updated": 1717507131314, + "link": null, + "locked": false, + "text": "sent to\nconsumer", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "sent to\nconsumer", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/figures/inactivevals_before.png b/docs/figures/inactivevals_before.png new file mode 100644 index 0000000000000000000000000000000000000000..ffbcd584b8d266305d54a6454921bbff818c9d5b GIT binary patch literal 65423 zcmb@u1yogQ_bx7ovbP8l0@AQa>F$=06r@|akw!rpX(XgO1d$GD5b5qN=`Lv{|L^vE z=ji$F|2M`R_ula_zQMu0*Lv6c&UofCpSgk*;orM;>lUiiQ?ci_Zr!%HbqjF> zau@u?G~od8)-BRoQeq;?uXJ}(kS`y${k)MhWJ(`;gk4IZTGHXaYZ{b3i4v?MTuKLH zu?T%w2g5WJ4{rB|RMOBqs)JaJPKM!Aj+@b^ON2?>ybiyq>2+yzs{;RXsdGzv-S7&* zg|D1#KVc?ifPa9WZQ|@kPC^PvO!(hFq-;oQQ3^e>dbuzdDdN9=UO=Dbnv({4LH_k4 z+zxq~Yh3U|nFs?N;a@)y3L}sH>rd$3p%@VgJ?a6uy#Km1{7xA;EdO;wQbas*(u~+r zG)1TXUZ$|9oZ)}1To{Q}LRgf+kSKHOUkik<*DtaCzn1LC)b4jn`;@px<|@zzyyg$Pbx&c3=o|f{)VGyiY~ws*&BeA~f0P%OD7;YC!^pOt;-b6R@3!AiRkPrO zlBj?+_xxXHtQjtx<2SwUdE>_R=EwKvOa-5ZrA-#TdXKN#c;CS>Yxrhv5b}(B5y_#4 zq3UTaN9hAc%RkQ5dz>=jafb7PH*#VCu`A7IF3!mpx9d!Ug$S%aM`}0@iEC9&;13?A zxO|)9zt}0y=G!f4L0b!^D>D0igOtt5WZ&+UU|> zs5P97YmF1R9IkY&?N-fmqA*scYuI;D_F+Ba*VpQ<7TBv^zBg{cA^FEfq$eP;QjV4= z5We}|q;e$uS%2*77TejZgIbEv0N$7LEjl)j%U4Gejn`vVjaR28U^8TAlht%zXY2TN zsHkeRp}+VLGu(J{^^(nEG`F+iwW8HVn%>R%#M+1V@~o01?v5rH*Q-pPEQDQy8BdTe z+%(@Dw6STasmT6bL$Exlk|^pVn`u8gQc*4?i@u5IWslzVa4*M%cE!WUI{p@aR5ru* z(8psJmq!C!U^|~>I$s`yd$M;BI>;umj_bNvx2zXNt@S1to*!=-roK7sQkQGT+#xYt zFcsZNaT?b+C2wV1X1CmIIQzy%|Ejw6TH#!av0Y>P@z_6mk*)$UlFvo>A`{2B7Q*y} z=A`Kz-=5iI<7qp=%gLZU_|6Akm49>H#_PX2?|SC({D+5_?5xYYyY=+z?ZT?WlL@`X zwaRb;uKxRYtRLh&(CUf#oUQtd*B&qLOqKT`AQKLngr_pScofj2+{H19Ya!NO;6#?0 zdcQPh7uc({%}|!)1BaihaY-as8Crc7i_p9tO3IcO?9;N@~ux@@GEVExyI&Je&9Q>~lL~tp6_R zaL|TZ_1FQNCEI$kgM5a@F!$*?nTM~4b;)!4ceCN1mxf2z7yC)%@3@=!uZ~BoUkyIc zdfDK_CjptwcoHx@V7J?LFR`Lku)dFLzFw!|oxrI&o7rG`fr{G6?-1hMjMbT}(30q# zlp?cJQ+qmXN#~4&Wjg!0pZ}7@oVu$&-zr|?Pc9=r7VoBo7&(FFp2E*0|M8rPajz|^i-t(Fg#gO2hlL8 z#l5Ye0mvfq+8jsq4B)X^Aaz%=}pA^=gdu zu~-466unN2M89QmgURI4>hQ1(leG@dvP1#JU;xUFT|1uXB*fi3NAGG&&7|26Y8`B} zM0|frZ!Z-dT*uMw?h{5O|3etxp$M zk#`Jc?1jH<%lRfw_{nhx=6$(nLG|c$|9+u{=-!-=Q&sJo-M}Y z&m0X1y+lW6)K`4co0GtewR1Z2k_kIV!N*!zG=0wfY;J9*e1KD(>&g#h`=EnlzTaR~ z&zE@yWHmSP;`y{gY4JR}KrCl$V$_E75EDI}C33^@$?0sYv|0i9icOtP;kH+U-h~TU0xjPZS zl1b6T>@GWmuJyUs>1dF;oiH<-(kPaP)%s%?3hrxm>}7zYm78K)=V!aDur9rsV47MQ3G+qZ^m$!Wy>!p*n+~&Lal`2}9Xqq^q&pri zWKTs+hr?!sPItYI+Mev|WUmFKpNLP`taA z6gH)yT>Zk!q_5f+5-p4H#zNKS%ek9%pPt{ND;h|>Kn{P)eKM-lw-`V|oNuK=@dwHp zj0LF~-z|Tsc0$~VVjQ)<=2=h*dirm%Vda?U{_fs^xCKf2s)Aw%scw53w3b_=pG+#oR4tV_ zWA)n~A;*=I+Knnos(coV!t0^G(lBrGItU0{Lf`(d2|jO*rTlij#RrAaOxOKHg)g<` z`RJsSCiN=rKLOo46SFf~;mIwsU<)~Q4Q?T;BTG+>!wglkm_199y6sdQ46RW0+qZGJzx-Sypd)nm?wB;ZUn|zQ^t{&~ zkmPGeF;ndH!b?U*E*E4#)7(~8%3G(W8L`(NrmRm(eS&(Gc z>16h5K({7sI!Eu>RKgHb=F zS$h3BGcRlctFwhN9<$!4v<=6ygODX`bvH_e@o~@+yN?6ucb(j;&u!w&uR=*?+aJu4 zSkc>j+Cgo#Sf!@_+*CeUJ$^@1!z97j>IESV>E*+U^>>z6S$HM(OQ+k<2U$CtwnC2< z5*(VGUr@?A2oVQ{*08Jl=ydM8OUFAR%Ppc) z1(wK@w`k2J+G0X3bDq~nBuC9COz{9}*37w2T|9Z^e2RGIJ=Q~j7&ViMFR!H3c_z0d zL>CrBaaBotRMFoKSB>jyh|Wg(h=yGm+uWeLop%yERFW;RtN0*0-Qg6dJB{_(OaE$n zI9sNUvrNcl_=A-7Im%gA`h^AaD-_Ye2R9_ME&&jfWuk&x7p1o3Z7R&>qPDy;I?-lD zr@A6>S`#jf7jyf)ff(epH~~)EUboR`bg)8$yK(7n+4d)qa2zj(3sss{{)7&#N-Cq) zl`@U`_;Aov{_JF?(aXY5%xyF*6Ul z<2dUP;zs1wS@I~dWTVDmYiX_6kw|qMm20-d5$YZ^b_DnJ_kAZpO!ga0^dBh%=M6#_ zrdJldEYPOWa_oyD72WVPX!M)>TH_COtg~hrQod4Q(xT8al3l+EvA~yA@&Emx4^u$! z)3hxM%~#djeBk-R_vh|QPc7P_S){7jt2YzeML`4<2tD{0k;$Dca~KC=&6<)QB+rP3 z9@_~VV=eAob-oUSoLbyr2=Q1zInAm$YVf(}Lnp0nyMuMIa#_7_U0ed?OT$Co9CQQYZVOFIAwfFO|Lhw&=t&$SZnX z^=Di&R^^-_h}trqhVE_mwx3VFTIADq3S!;dXLEdw2I@U$oA(|9J~p}4YrJ>$8RHn4 za5Ss9Sh?4hB?b#B2mUQhEp9bIv}FnYpF!zlHbTiaJY=USmLK-Z)=;<$@YT7RhdU$5cRjM~ zH$hfm)L5J>_g`>?9&{c+^SI*SkYdsGvgMtud2c?~A2X38T6bhAQLF3I99Gi@e%u?)eO9 zi@}@p-c2lNe%XhA)NlFdouLXXwPIfAac|;^*?C)BHZ%FJ_mS5FYXg`@mo(kc$*R@P z1H_SU$0KD4EpZ0sws!j~d_T)p3BJFnMgEE8qCap}a#Sp?y8@34eO5-&_Q5>jCUPt( zg`nFP$uXxq%hNYX_b?urLFV4_v(eL{5BW(wbL3{l6mn4!VkCyi> zpY&pMHVY-8=08{1Dd~WcDKnNCGdl`-lR(;+NThcpQ$BpzA*(wcmO&k77vez)pEt>r zU9#_5NwqIot6q^z072*-M}#N&mxNr0OA+;ZD6T`jG~N(ZKTGD)2lrWIR|eC+HtAqA zb=jfok8}C7?tC7R-|eQSwyc2O;+5HVv@*SDwVxa`By{Mt%}vF1a=4gh~uODOqq zX}0AbzJ1Z6o*${Ip1ONSbuhTciE$yU@zKJbh+}_A`2$~7f%Pv5bwLrVixMjq^Wqf^^cH>2fSGb55C{sbw$ ztVt2w7?knGNjVOFh>|itE{J%F(W<25F6V>xb+_9+YiOcG&eTtD9culH+X<8;WfMiE zlm0$S?WYoTyTl#oW2&n~(vyPxipfQZKsUYbR-d`!6KFHh$`7id)w~MQ; zL_OQ3)Q2J0O{F*#+97y`^f*6)<`Kp@3u-C)&z^+Vwj%mCgq&nK81N+XIOM>&nx1!? zMLKn{t>iZC*Fyw$)9IGKiMI3@_~E-Bc2UFY4eJqHB=Q&G3iv@?Y~#1%;zQ5fI+kXA zrT9!s?=ax59_9?}OiJP({&;IV0X@(#mVef*LfnWjf$X36%r7`p9p8hiq%gXzrYjl_0OM&-m} z_Y*ZYB}BYiE1JxL0?D=SmNv)ARENP{q~vE|d5ZLyt6u|xV}_V?52>oopEaC&DNw<( z7#Me5y@8IARYXY?hFlbH8?1k z?sgY34UU1-vs2w~Kbi{Rxh0|#Gc{V;T5=;LJ1ABWd26a}~~pz-Ytwom)zwpwhY;i?uD>UsRk ze!W{UQP4w-g9jr#$Kjoo?AbLBnR#ZeE9n*_7WAl{DAM6uvRCHZ4t2{I#>5xE=zN?b z=JXRVm`fQ^-Me-0BE>yFvGF73nWVP5FUs9xvJ03mvd+b78R^&RGaVXVNkR0@p`h{w zmWS%mK?Zoni>s-&Ef!DfFzO&rDgQ=Z0G)fkg0o?h#jQV8cJh~A-V_wR>ihHt`@I!C zCX&0bGSUDn|CG4;?iawban{22Lf{NrMKUGox~{dr?8&INS-2R@{mQT|)+NmLKytr@ zZn|QtIUZlt&m|WQKe(;6K&8Ap5je^`A<)X{b_Rhhb!Y3^`Up8e_U4~lF4jtYz2EQE zV)6dOkC`eEV)&Te)u7}WVxoNqTE94pvHTfBO}YQcliZ(fKhZ^YA`PG6iK3v`)d;ic zpEYrerQP9dJ`eLc2o)l#9 z^xj>1sO~mm+UI**6-vK930xo(aN6P+!)Msg7nIAk%heaBK5t7+?{R&xC=l7g{A1e? zFlcxI;hTA8dwW`PMFP=8+i6UtbWh@y+i4kwxIG|40%Ig`gZ^PS!7svefC9J7{#N9m z1^wKdV2kvGY~ij5^<-7Y_2W|_ST{tp6qIXYHB_{J@&v+2hjM`3p1=Ay9rEYZydV!F z0Edjgry}=XOmrs~SlFAhGz+HRwFcbg6@(EnE$-|nbNrd-Kygkwk3=zgIOm!72lj-g z0$~Ke`(u|VjZysTxzWAfU;!%M<>u4rZ%_I~;NPJj%~1U+uk!wPEcsRY|IZH~yyiCz zrwV158jKW+OWW~t3_ISf6;^#?1-$k|sc?|i_ zJ=L^S2~SH3*wu28izTXt!p5pE%Pt%h`^3_npn#DnENQ-%{cX~wZnt-#$+r(scDm;U z&(Z`)y1J!Zazm$@Eq4DHpj7`9KARhype&GiAUVQded>Xz?a zR92;r@*IG3PUZnrB`uW^P(%J#H*p z2DgVNK6j!-zeU>mLjiNf<)mSF%&VAuwb?Sj&JnXY@2G2GZ}kbZ3- zC}01?V79JHs-}#__W+wi^>NP$`ISR6l=`#bKy`+0$+}0DL>R3WQ+M7mL1q{rE4i4y z{K&kr9&9hM8>qHx%&;H8+!EKA4KK@8o2-6z#8WyTco99hU$dTS(i(`d`-&y?6K>D} zhsWiQLD!n=Mc!)p1Xjx9sy>IYFs62k$$ekb-D9hN8MXahQ$4pVssTBy23eXx zlwq$YYc|9TBxz2-;M0qwaagKuP+(=OJ+Fbjz42L@mLz`tVa_NzC__(m*0n7`A=-`R z<5c?#4ZCUecWMkLv|OcON;u0yxGZ+t*y9qOFgd+AI?E9`t_mE3xTVlyd+S+`i(MHU zvu+eR6qW$5&_tv5$8>u}{WnDAdGLln)dlLEZ55kE3i$>fu%|m$uBEjC?&rse+t{XW z;Q!*YKM^7|3`pj=BHb^{D>6t%lS>goU%E@fXTqD-)h5}fX>hv3#<-2MHHxc^S4DDT zwmUTaG>2IhkAk&SG4%1>m3JqwY{`P{iE4DKAZ~s zGsn#LwR;s4x{=0;#mp$gXGX(Ys#TxVczR;$-a$1d?{OQK-rIX*FQ)CJk!6qgR=a%!tc_#`g+}SDW-p9cY?{S{XG^y{Otl9DTDmiJ`?pj#xmwjv z7~B<4z~k43y{7<}br;xzlBrXWEmqtz-Fw zN@ri}2a2V&ZYz&J@M~FnByLJ#x^owo0mCAuUFi{Cc`MZa$ji4739CdyeM**j#GWFFrE`KjR$-^a z_(xjuzQH8*BFwnpIeK~d;{ekV9~%~d3DRJ+aNeWeA2Sn21WmP4#YU;ycke5tN2(Gc zSvcrGMkz*TySs7~vqT?=7s$w|260)?rdI{;ytfbc$kWd~wZLO~WSCE{yxg$f07 zc1yriRait-rK-CBL37I^L1?+0wv;d4{80S0W{4 z*ZWhn!V@*Ysr}RaE>{tB`CAL{>yJ(p;5KfcpVbRmiE_od4Lc-%Vz>Y7sUuXGVRX2m zLHfUU8vz4?37YZo|2xh22vbC>_nY&Lji~gjuf8wvbBR0Qdgp+zThe;WY5zWqVDYa) zYI5G|!DCexa)!`W9E*Z?z(GY6i3w(eYLk9%-AeE+hxSwfw`tx7B!b#a>-J-Ac~-HI zR!oaM(aZ7?lfJ~5 zwDC_lzqjkJeT#U4r~<8kgMiJEkLx7~d1b%X@K;5<5+F>U;lE9;%7IqB2YQYknjin@ zJ^ux&gwdd2Wjt1Kd4C?ruRGLG0xiN!ky6|5BmMp>Ta%24e=i8)pNZO;{S7WvM`Oi19rV4W{s1WhsHyL`TSn=C&Ne7S|>y=X7 zo;eL5daH}>wqiZ|?9>22K)HhHn8=^|wv1%+<);bgNs?wQr?7rg%+oc2iruT|Aa?6L z-kY1@d;!|yIhGSe3D%*sAh`TvZxKX@ffbB8wPx4xDU6D8_G0O@8JwW`JyNJuMac0? z1^W;0HM{Fw_06@%3NYNK;~;AnY)+OMvgKvC1q<5%oVZ%Kt!($((>fUu=ILZYuBLXW z$LQ+-zAT&J%1iitFe|aQ7Js?{*tE?A_SQzZE$J!g^IX8Z%@lwpOl2^u7FX%VKjjCV zc-~}B{Ns5X*5Wb9B~}X7@`@nrX~G{AvVsFSqTYlg&FklR|Gxe&{CWUC(zcFtoZm#% ze-7IjE?xP^q>S}@#eexo30NvS`tLLNt9JzqlVX&0oGkC%zwh7$w`M;zN~hgvmDYcK zi4?~h-a5_=$JKqMU$E^I2?_jq0};E&aijbbpo3P&y;+URe|;kzhLDn#g8)P9f`2T9 zHtl|i{qI;SOous0ScdJUV=ez4JoJ&;6C*_#tcLF2(BFFrbAj+1NUSrQ3J#r1r{$We zLWq%~VOrsMZ0+OedW{|@`9acuHrQz%BDCVWjed5@QJBkXXAcBUGM-+$!TH-h{x*~w zV4OMnfB)A%|Neh1(h*jK(8NDgbbY=Htm|n;`-1|P{cn{vN;$IT5=2hV{x}?OLQHkg zQ?wfG01zYzP#0?@cYvd_hl9-+(SO^wO*p6V+anI8g&jKUPm{M&utBQO*o2Nf`TdXg z#1`NMM$*eKHP%Bv4wmHrjJ`L@%OQ@m^$ghH;(0ZouO<^^(*zr?IfjY;JfwRf3z;E| ztdPnkU*x2BM0_}zau$9N_F%9GOJ#9Kl0O#wpU_MK49%S4H?j5~`}=<+okEw%l}qLx za@-n~(pTZo{d1`XSdVz@KZKpX0!<*gEki4dS_LEzQuN28mo4|w+)p*GI@#_2^?3|+ z&MOp5Y(%huI5jBg*HZK4SiwY}*XAE8pY=6v7Q=gt%PG zU%v9!|EDt|jcYr|9Zi|0CH&{a##f1Hm63v_!5$BN2fIH-Uo~q#ouxXenEq!3C4%&V zDA1e0)&YE^1}!`PDFzKE2!Dl>kWp~2KOz$Ny%PuIc&n2D3-Ujr+AljDavN<0SmDn# zf#1_hk&F~A>0i#t{r5k?KnxRy4d404xWO+K=%n$TGcOwp1H&u6Dy7C!gYDy%`vPo~ zb8A_SYi{)LE7G4gASL$hCWAFO~Ln6`x3wTSQCsSxQ)hulil2p2ew=UMc_a@=sDH>iDzbS(+a0UG_M=C5QKHFr# zeIbw3%WUbvz~tvU%GhIr*-jK+%VSN8Q>&c(w8e)Ak6K@beZTC(Iag8EiVGH|0#UjG zfS8Ws{rP!S)%D!5j|1q`twr zd4Q!O0u(4d9W&aBqVCnNM}xgw!U}w7QI^{lfVVD9J!pGJSnMsOAKv>rrglL}MMYhV z2DT>xTvyB}_-+LnT>#9x<>}tSGpo!~ z@VdSQ$Y%XG7*%BZ#>#T2n*wQV;fF0?^dLjC)&Sl>@ID4O?LnbHQpa0GBDNbRDn-{SVIk{zmJS7;$C#`UP^6` zd<@59HF}z|munCr;iaNSmC>&v8C#KgQZoJC@7BO`*m`ZN;OG3JT4|g}3I#3tm$vtZ z-Uyg#S)u3Fz#{BG(stS+Ro%ALCpV9&l8?|zS1%}2pP0So*wwJ~mRG|fhyEA}tcfp& z*gc4)driTsgHGzTP4Cuu)nMt`pTj4sUcI(~UlWtlqmo?#M;2?R>KOfx;xMz#5U**8 zV5n-60_mAGo58k-^((*wNZcgQP1_d{KOKA+8qP!dApl3YlyUa3wRewLGF9wy=N_Yz zK0+N(*DH`q<<}UPdN}#K6>$158bT<%lo0a_G~p1Sa5oKIfgo`d`#Xy-eoXLJn9l4#&xm2;DRR~ZbCO5P@--+Xftqw_& z$bC8{M z_j$=zbj*>lb%u85RZ|QH9k@Fw3XopF`Ww|lVv<)R-h88ZPgQ!ZR>TM-93rH^$NsvNLg1C*HVKMFWfmVK`~*^c$7i>4razQepqA_m^w-jdkluv9trv5B1GJ*OKo$B84Ldd$ zE^v3;|2FqUsXWToG#R8hLzVSu%VG{7ut{ui{*N*&B!(#*x5l*K+isU@_&l@WTXAu5 zciHymHCk{~?%;OY@rU7$h3{%}MpI@DBnoU=JBX7TRa3pXgALnqq~WHomkzldHxwND zI3}Xod}B~$`ttING++8*_cQ`ce4+StCwVwKb=n(yBOQNK0u$Q?D!WN;$)SfFn|Mj` z1GQ~d8dM{TpPp-mfV=f9%aldxY`nA|=&^W7{&Pe3ggkvC{KPOns{9FQu`24kcm&&r z@3pUhdi{R$24KNy5CB{%mnrh=OZNa&L?X~s&VD;jcIaoh*saoQvw(}kD~Ub;?2wop zr(26RX45Hf_s;~)ZR!ssF0SCZ zA2_u4eR}nC!1G(q{nMBRU}CB?40StYCaJI2%#W_X{+dzAC!3-1te|iEOJXZQInW;?$W!GpbrK zW#Vn-YNt3&>`3@8g+L1^DAcc!-++iYTp8s4+;`yc^NXs->v)Uv8ww|s+N=(M($gYG zF3IGf!iV^)nXSS@oq=c}`+61I_vs;4QGkHkDf@uH zS@M+m#}v^zqU{+GWFn(Bx{#0RJte)7=zN6wURs$B;*JA%{n~Y0=EjO-saLw875j}h z-!4HgD6>gKVfY*|1tjWLq>xOF=boqKrNw>E#Ju_8de8jgFPi7^=jcW~HH~w&EvReJ zO7iAvB-6HClyQ%6(#oP|4GLyY%SP$)LLi^fK)v__a5dvhJ@7sL7pVfQoE=20w3BI$(AH^||$FVS0l9LEn%s1=L#t`Fm!g^2H?zdoicitW!6z4-YC zOpq{xe5TjM8-TJ_yIqu&P~>c=Aj-g|lNlT+ECX7fGLv)ix#r;tq7mJ?Sl}rWOCBr9 zLsp#L+$@vaFvF)vvg^BP^CvyYX9K?nssuN@XtMnQ;&+h~0>$F_hE^aR4hTiCwxM`&iY-X$NPWepT9c_ZEVL;3qoUPl@hiB?qFcW{z%;}X)ZNoqB*tYgHOlw zNl~X5y%Rh)vuwi$^*aw}gUSL}Y8}bg359KfDTS(w=41kjar)K|C^h^0C`S8us;sAN zk?7i>by#+@W`^(b5CPU(;|jYgrH)*47x$SMp|{dxT3i4{Mr?rYb0YEdYLGfHPYeAt zMZLOH1HGChshdCc;!#JaWk{5SV##wiX*T#^ko1sISKk$pOg{M(rI01tjh3_gj;~+h zrK4zHkWhDPTcj5nT!7oRrq!w)=s|A|OuSa0z%4sQmG*LMr?hY$v~=gmf+G>KB#9Eh(I`PZ#O0` zsQcFUw~g(&J_zG@YVWLZWWaG=XGbU%_5dMmNGHW6FH44axy~@ z7)zpfLZ$;qJj}!&;?NzEkK2}tiD9Is(S#vkH{tucEq(RA-3Jn=UhP{xe`Ljqo9hd$ zjP?^yqkf!g^l$6CLy^F49_17!hO~4Tu0?^M0H1VfajACik7sH57SLeQOQda&BedrL zoNLWs5}f)zRXb@>p}Gpj=06B41xpn#2g`!jknuWwBxVzC|KZuv-NfGY@=-bscDIdu z@Tqy|2q%`>L+{YW2j)%Yt1(@$;4sh|t9O0#ym7z4JF5eli$06|Le*~RNl!m8R-*M} z@Mn`ax_+RrkGE~I7s`*t(}+^!uvTIodi(d(&nlZF&>f?9eFAMid<8s}W@U}~c}aB) z$}UNOQdV)-_9H9ykJ4u&-V+*IsAH5bf7wC&Uv?1h)&9c3kCrqG>5x~-InxqM0aPGx zEK&3;!RG-ACYR=smLW3gJ+|(x(rcK&(THG6kh_?&J2K?oA$w7N?GK2b@A{-w`&N}! z>k~OZ6$9N?Op>fs&kJ78e;If6{|@t^FnD+RoD0Tt9&}}mB;$?ZUJJ;QXvDNu{gGbF zL*BbdUG%SBrko{Ye^byolmHc04Yu3c8?=2*xu_$~l@K+(u%@MxPnyTK;X7!@LlF!^ zGy#)6;d3c|Et2UNh(fd&Tf}Fg^e+iRQ`qft*$`1MI`AySyu<<-Dn_4=zh$A>gR8TT zz=Yl8J`d}{_m&4$Dhp-^nNl!fq3a)pZfFGaN@_moR$!!zGm~+NO8Q( z(rKVf5xhKp15zu4hhpLDY^tupseKKLiuM<=u1}n#-g{AXnGYAd)Oa9p9I;ryvQC9p zPq6pSZ%fbf>SS9hGFoQhkt|+rFyMW1gMk^VR8Q*_NZG@E@b})cOZA&|M1R6THZ~N{ z*CRHq)3=%4CTzX33on79Ik8BIS;vToe|f@@Xp)`&}$@WduCRbSZlYzMQvt z*({TyDTO(~!gm3lrQCV=KYh+=BO?G_>Sa!nVe1wLjUc&hOo;=!wJ z@vjY@j_*txAQQTKVCK&7?KEir$%4yUFwrdYP>x2qM7T+IxwG@g#0g@Ps8Et7v+aMqBE{%1^&0@&W}Xy!-dxPjFrSa*fXFy~pO}A=So#a-$0#QW za81Ai1}$#H9sPlfiNE7T#Z^V}U_1Pr*%*G4aCYFan1+5v6K8USX@feD#Z)T1jNNd*B3GR}bd(ID$)KxBaT zRw_u}=Oxl-sA4nfXd}aS)hiFiCX$ZrHw{E7mTO=h#{cjd3_TjdX&AQwphNE6sa}4k zGEWlMQQptnqYb7iO~8a{5wE8%D|bdtn@|05{MKW{HZZ^&TOvd`iuubTL2mhFkqV$l z1tB&GjLgl#$0+ZriBV7LzBv$g0GS{Gu1qKZ3FOmRKLFm^(`x>cybE5p^S&S>qFPP^ zLZuIIXIf6z^+rHxB=hW3DojBZtQmTtBFaD}Z2U~@mP-`8)e)QVLMTXq*5NMnp-!&A z_pi6tz;IyY3Mj~A9RSpy_Dr+ye!S*SV8aXqL?y3*q_IyFg(2Eve-XgnM^Lv)SDxba~x(jB_Dq`7(;gh-O3Lgq9r@QL`EaPe*d_|&IyCYiT2F7C7ZyT4+&*^zmx1m@np3rs!6Mo9n~^LT@;LPo>bLUpcSPFr3oA7% zrW_m^{4@`Xnva%2K~r`C!&ac+c@{IM3Ab)0X|#H(B@B$z=xKPvIM9p=xV_2n1Sr(N zqxDrKH(_##%@V#|uT_@H1?_M*|F5Kk+znvLxP9*(TR(S}qt_2G_N6tl@^j_=vr3zA zF!NWU;qh~IX3niYnP&|E7cRXw2X8ANur`174;UpeEGJb-1Ax-Gp z#0wXPZQFC34^t56&S!%jVh^ExCz(lM#kj=?vggkpy@D6#pr)~Ql$?F|jBl6S0aQ_m zV1Q@%TF&)kOwHOBT@VF@@el-%B+6LzObSRi?-q^2gpNxBQ2Ta!>`|Xi=-Xwz1GO>ID zKIVOTUlobyaky!bR4%3GQ|^pPAYDrYjZ_LI10Idxjp^^x1u>Ycs zvG+NFyZNSM(q>Df=ZfwE7NvZg9nl|7r=0S$!l-#s@C?ks7FEDP&X|0H9RY=auT3VU z?Q7P&`yKf8XSY6xg}5j^L&;af9DoB(Z>)4S#tW0MRs1g2J=E(!M^Z1r@EuQ1*!O85 z+mc3**OsjHPzSVfA7J`-JJi3YzFLAF0SR8LHvW`h6~JjT`&VXB{U>4PAf$3v91I2e zP>eo}`qbso%sZ-}Hr<*Q~?u`Qjrlc7tE+-eGkPl5_%@zZvLm5F!^&Tmhc9ht4 zBjY*EH7KY@YUvExwkC?xc9D_r-NtO0C?W!}Ol{~8i~OtE?%rmbwd){S18Ta8*CjrE z0R53hp}X36#xtGT`chbs2R+H3DwascXiZGx=JetvZ|)bou)4suiH69DPQ0%sS#Met zl@c8-1o3kX3@bg>xU_ZO0RjZ74$T^Asod4PI%N$E8bWx*r_)VK!vxE8Nda9osEm>` zsBA&xcdB=E`T_w}9~dM*0x@ef|Ipat0>spKFo)O|jH?ahnVil(Snf!ACUi9#oDc8N z`)_t|L|KCKwOQ_Ge8RoRi6^^*x_rM9-kO5z4Nff!bY&PSqOZ=moKiW_BK~$_1K)?SGa|fq+Z5b^n z`~LQ*(}a$x$5GO}dR2#(O-&o=3#n=rSTjwMC+Jw`7^OTBW&lI9Yd}v-uhBx|y8wev zSV=F1c-CD#8GX4LqgENqj~G|o+Ds8i><(&|s5Ts}-b!Pj70VXy0XqLS_$;>xfPCdP zDe9Al@j&A`X_;3Jc?eIb&0mz+v;1zw2w-@_P6PXdZZ7Yl;J8ixXp{+L zIE}|pC9x!c#&y4!=l$e}>JxTzF+^Efk8#|h(Z>BA6--TybKkwy6d*$pNm*R8hJQh{ zmjHP8yo$<2%&xOv0@is6qNhlYd4Ormq$EiUz6TFqwdF9M$dgMsnkc1r1~da8JQOU* zC2{tp(iEEs>>`KyC3=YUQNWkW_Im5v6l`#7IqAv@LUx*faR_gaW}}^hl!Ur(+N#Bz z8(6mik~Ld3>7f9EOmmS#^QY<5^=l;IMN-RI3aLQBRuZTsiK@V9N9jD;HUW+pCS%m< zJM!F0;*&4Bq6|EX@MbYFvdHK_{hjwx-&|uz|I$cQEmh9i_r!2gQ`Z@n+RNl(=L;Q; zC#t4(t`#;a&u>A@-Vj($C4v!kr`Hr4lCf~a^iqdR$EdTU*1)L+Gl0)z91HP1B$472 z#U9D$`3cf2e~=+eBAi&q9if{HkwF$-gV=s$P6YB!l&pI%YyA@a#n_-`uo>C&T9*c> z?e%leN7=PLzW&6ud)$2kQjwJ{7QHJHgUE}em?zOa_T!Dmd#Soo*GDR%h0aBZ)MNef zJmTQ0JRzDN(gyjh_&6ue=9xO1=n zp6o}X7#Lz3ybt}|yQ8WaaAxBtAi~HQlJbFSu51K1Ps$y@l(B$(=`etGar&OBf%fWX z9GoQE-#yZ+rMLLG-@;!7g5f~J$J!2nFmM8*iasF&D|A&*NdQdv7+|aO{Z%_F0Ng^L zb%Oy%4*LGs0S#PsHU0fPO>t<2k8?pHB_hu+;8&ikko9Oc<0(XGDj(cYm4nr|6AxT1 z*-70yk1j(syALiSL5~_@FXjAR33ygC}rWCj!+Nz#p5W zDvue&z}akm-R{m30d4({Dvx1&^9M)vrGT#8f zq{HavcI7c#3*PmQEWJ)~H(;+v%>Ew4aGn8SRDS-e9q5gleDy-$MPVWOK%4y*-l+## z`*={m;feF#Bf-YBU^uqydZ9!)$~yTt?|B|r^YgY(&3UB2e(x0Ai`ycsaOBR59;X0) z#*R_I&rC6@c@*$IMs@ovAtqHpU`rg7wz@_tHV?t`(}*5?aXI=B#`SGdHlCSGmr4f+ z5njM`k>HgUtRo+v;a1)PFBU=#GJ$I;sodx4Fu?urMKsPJ@OK>bS3lymO^5SMa65!I zO?ltuJ;X4>fY-$W7GNBLBgv{P=uOCzyKnt8LoT1QPRqh8^3c2(d~o+sg}iiHU@j`q zg67qi4JWZngdYNwC@atbg*GlqZ(#u}SZ*@{t3;mTt<@KNZ6_dh9U=+PGT2u5X`(pV z`zO(4#4h$Ljy)$^NG%0I1!n4GTTxo$Jamk}C*VqG*&CJ*Q-u;Ei_pF8fh#4tDL8Em zHt~bE0m1OVljM*M!RyYx55;a=KHxFPX|{>=!SM~_;R3-9(L35~4){qgD4I>5`nCdh zIk4^{QDoC%_&f3dEsamv9$AKJBg=wE3*gdH@W2a0u53O;iKlyk8%_f(X9Xuej&Gyv zyF)8Ld)j`K0(g~EY@X)dB>6ocT9QFn@(fv6uw9GVVc8uo)cS2DsA%P;1Dr(Q%{v$r zV;mztb>GY2RBTOdZmocY!Yh+0IG3|~{n&n&Vl;TR&LRo2;tRBXvEe!FA7ih5wM7eg z+y}c|ViYV_zr75rl*z4-5Hc-|0m8W*>@g`MO?VV)zK;DrJU>58G~Q3?D$xpbW*vDT zkdVX8i%;gi&Fg;+BnD6pw1z$K@{xCmW7T=mxg75*_C`~|8@(jNhk(UnHw2W=2tq3y z&)2~FCs^Q);(l?;A>exa^rHgEk_@6Cn1=!F<9z~7*~Ond_aV>l-;6~{TT;Na$mF0k{r;*zO=h&0wtHGP<%m!swjh{K<4=e_|TViCM^>qaEs~> zjwyhAY=B2H*STt%RRABesgI`6z?|1YlQzFV{iQyW%RCnD}n-}QWAoI)JS(qNp~vU-L0f_cXyW(L)Q$c zbVxT0-8qD`z8Cx zBMLgz0j(*!lJvhP(|Mr3GYq6LO#Gka_E=>4zjsj$=qcC3Ny?FfZOu^LC>ZG z+^~>^N&ukntQBPOwVD3cd;s={&VzUbAs2iLUEatA zAdt-hC?B)`&uz2-1-AU-!+o@m;Tn~wXoZ9i-CH{#>p+rUa#}X}*Z(M$o7y?Vs`zgb zfm@PS#z{qHnjv%0TtP_p+y7NDI2uw}0*M$ps50Mx>o1QQhV95+V1Zsp=7-oQMDjkoU0M_p@HIjm~*P1ys zUIEd6YHp>qT-g1Q%3_Du^T#!PWE>71_lR==d+<>iU!(Ax**y}cF9l>m(2sXun9jO%5Je+Whc0BUE{ znDjML_32mWw|S;3aL;#B<8$-vrgW=`9|DhYx;er)9zqQ_1emjukQQ;Ek$r5A4?vM| zalofK8Ge?G0knPI`6K1D|EYMiaI9NFFMx@?Cpfn{Ywbth1AXxV#``%Ws6WIbUvGpj z4l&BN{Q@kDp`7;-k&b{T@5n&{ux0|u1Je22rv2C)KyF8{By-mnQKkn&Kte*^KyIn* z9|C_ybS{YSyU!ahUWnwarF=NhYTiH-iIUp`xX}fPg6?P+fZlo)Nd!Dn#g%==?*G4- z`THzUqJqnp_{I`g9))gY8X(!9>VkjMm7>ak5{koM!55T&c|L2sMLg3eF!#;0Ww?3H51t z`W9O1_OmoOlmt%2z5e>k0gG?-%pcDWz(1jH+=eZl?ETDTp6&Y@SC;gjW{swT=|;U` z5*9hWv$t)&f!y;<`uvpNiYm#fG-LAlu0Xd`Gga!t1QPxWfh`qd);tO3E03}n1ezEJ z%Y?u*MZ{|zfWDapypbV~yM29NF>R)rc}0($j!$Sm`kP~L5zz6he0TA3N*4+dPo!LYxN@*1?ryG!mx5q`l*Q{`#F|3?N|zp+2TA!QMLgs@F0U zBf+OXf>-d;CH^Wjpx4x}^92D#&7hg$UbN-#I{EwGlND)Gb93bgD*A?!BcW+5fr}IgQ(09h_df(Iniw}N1 z!(zms0y9};IZ5ub4BR0)RveYMW60g$PG(*q!~X~kzSqFkBN+Ywd!D%J7w6e}|K(|x z!m|ufKJWR|R>3`v`c)#%Z?Q@e>LVbqpA77=2Gq~m!$-338Q|Ydko*?VRd&2&fDjWP zdt;raA2c-L8frK$94P0Qq$oUThunz^A?FR^}mq zGK_8Q4wBE{l-UXGJtzn<*aGdXb%kaElf`uLYh3F!&B9iPxmnnREJHFo9xSmW+ZM}e z(|^nAWkg}j$H5T}0)6*2ZmF+DDvg8K{=C5WA^xEc-WK<^73YQXCXY;Tc@S75LnQ_x zdd2YN2r{>JfR+b$m@vRQ0BXSw5u-y@`)wJr*Dc!O_u_3QR=kFsapE-+`{7>t`q>5e z+)|iOa!-+3Q!|BQRTq|UYsw-%Wo)MM1QBXo0{WZiPaHuDm<$j@HfLz<>-v%HFlY4! z&t_rSl7^!@-en%7n6CwdZQ0hJAmJ~9)#c9~U#XBWmR380o_*%$e4i?WZZ`2a+)oVa z?v7hB0Sm>)hKL;R%lZFUwt-7itoWmi8S%#2HZKdOCY52PPtbiA-NWK=H)3)mghig`uB5^xoO+f>ns<|IhM1` zm4CfZnJ>j8Yn3+u1(ykQ`5@c3n-qoQwc4UgSb{%3s>|h#iw0Kaip4**M|j495L?e`g_&!*fwuj7!BcyAwNR0NKC$m#TzTX#mN|h zk-giS@mQZE$2XiTEkWAna|a{Q6l0b^ZTj>C_~DW*klsb0ja*N^R&*kOEuj(kw;;0Od{m+x#FbQvtUI0k51H*FbOyR95jdrE;`R+2x(q z)|LPY?^sai;Q}l(N084iy`V0#Ww3wSXeT<*kj|Gdf^+czt=`b!@{Qw@w z%0B=KYK(l#D8I?XrCkD(Frh;at9Ic5N2d zTKKQ5E`p@e>}lE+o!4ACL)=_ph~*-XHL1E-7&7~^@5QOT0g!-38H&{*+$}guNx77j zrz)wt@9q$%`3o7HNa{hZH|CD=kfYYxTK$BB#gqgKWGwa0NqjmP*)Fa4Nfgdbov|P6b+%Y&#`7*R00l!&S#oI}7R}z>eAp_geRw+pQcPsIvqxNBHEP zpHT;~&oHU;VQYs`f`=jC#IU^X?x|t`HU@-3?HVY+O*Oq$g*YQYz3pW$M8k`TTK4F? zuKNVqFG-QuCK7*+IP(8J+~1L?H{N$^$;cw38I4aSD76_Pi9F<@39OHKk}sdmYMugS4eJvE z+SD}c_t;CQ6b|0gySL31Z!a)uDf2JIVtX&zo$g1)xrx3%lOqACekYR$5>cD=1h)8~ zz{Ig|#VayQ0C*n8C@ugcXkjHh^Cv6eTY&H1^-%@SB8gNOuqG`eZ6WeEK--w;KHYF+ z1_^*#655b3Bf1c|Ni$GxJ&1=}BA8M@F} z;KW_HnXFZ)ju3!gWKkRvh;a&E0o7VR`nHqvuF=upZ&L z{S;ci#@2mh8^7w3dO~E}E^2CnyRvsUb7H^)Is=R)?Kh(}fEZT3PO&zv&6i$PV;TEN zOgfY%#@*PbUzG6N309^R8wftZEpUTsc#)I)WwA?P#7ZXTJeXLHEg`R_ArEc4B%s zME^kMbMpI4!Z);N{&<9;$cL+uT#lDA=A7mp`gWSc%Yh|Uf!$f<^FpZ=C z0_mjV_O7l2zABqc@QxtuR)b&?kT|zlR4wHl@UW*@{T%@+_^X`}3pf(9sPyyCjA?Zp zCjBnT$}?XV6_(*I_L`xhK-sllg9y))hWz6&df7a}dmv2UgZng#w?nm5;Avl-{x~Yl zROh^u#|kfl(5PM`@q4K5`qD^H&W11CXySVxYUZgTHujFyLabJtr-175Jlhv+j?tTe0~xB$h`QQywnvx%r|VU?8+8LWbh% z+>KzyV;p0r2=g(M--c!}UaZ$1ew5p~+1F9iJZ`%Ub*`(+s}LKglJf%z%M&>JQbZO4 zWrLEt#<_Q&mjw&=snl!U)hB@s{w4M!TqA@z7l!(=K)%ts%D0Zsx>ni6X<3j0io>OG zsxX>MHSUi$9a50rBbX`mWcIOaTaH*h2Oe?~xYnpVooHOPS>6B{aG4(VX~Sb5aq8qv zV9}F;Vg2Q=9#~U=LF?T)n#mL<4*@2)63G`FuD@d#gFz~Mc@#sF#`vh5&fMd+ z*FiO{ABe?8kLTFQBNrL`yxh3-4JjwwQJCllYr~?CMBcUfVGyn7R%}1gY7QH31xY6+)Y*VSMxgnx@Oq9Kw7L-NE>cH`t1{|PI3CW1 zC{SCg9H17A?xwHPiGSVvu2{u~YLGTT+2mVV4+S_{pvt|Hy~fbj)-e|?Agm)ZUx*1HMU4kQ4@D&TA;dZ04=dM?w zR__Sz!`l1K1glAsz}3TnYBfzbPSf;#T5Lzv9MeN{ZB|3fW6KKqV{cj4dG-o{K8L1f z8hpdMYsG&3PXpsEJE0*9u^H=!(`VakGg|L2m#(jmoU{4 zNDl;Nm$DjXYVhc$h^pn^X|4?~hMzTvmVyoRgNg0LFE$?fsZ)G6M5Q}LC92WXTiw$_ zZp_XjLXqz|W~4m8Ns+B@ccOIlf|#SEqvBO-y6jpc0T?=`Hf zubs9wz7JZf+rHfxsq^#xwe{NPTO`4h|2cg)=UWSMEYTGSY9t|3(Qks=$&u-K_V1NW zR{4&mGsW#%1g`@BK*41o%`%J_s2ooF_$XhdiV&!!E3w9;VO6`mVw6?=Yw zhOxF;AnU@^IS||3#}y?K@Ui+=Z;rd}`gPP5kl)7Z#<`6muruEzj!oB;cMwFJg0s!| z=aplZICiz6B(+teavb*SEtISx!OQg|sV}lS^yKEJ;h!ZZY_M(qzC{q{Jfq&$hPt22 zpX|gsRb(Ztwbtt1yEm>9L^@)5&G6<+jzQ}QqTndjd^TkrJG_7Pdo_(A@d?loNldP$ zGj$g7lcK<)`I~dbsf6l{zF!aA>ZO`*B^^(FSI#7F1f)->n46l1VzX%7(8W+T-DM!h2Y z0$&5~*!1?CbVvSWeDPZS$U@!=)p_;05!207efPTU6vYHf=iSxo9cw9%2R8iE60?o6 z0(7pG;>ZE&V?BG9kCAx-RDqGk(~!B^gaf$il8`RMAn5?i%-GL1LnI|{Juij>yfFs& zh%Bz|uz5hV4PW_I>oekPIBEsi;%n-us0&tfes#*Z(UH55(@$^A6Y4@>yIG8HaPZ?S za3V3#3k*fO0ZYZVOTHbJ*Mt&afAtw?uTh*qjhY&A8gQG@akrv)&7_tY#n#cv>RtiG6e#gA324^;*wb>$BDH z5XYGkV9-70Iqk=WrjRhy$FlXphn0@`ez{9P6Nx~K+lFyr=j|_RDP?&9*$r#denMqp3vF;8jSZB#_yhX+I5an?&q=*(b9ROlr<40o6+l@q%0R`yv1I2 z+HR!pH^dwe`s?MpH;1ECr%@sRG#?$T0fZ9Oy&A$FWwdxzsLovS_R-wQ|K1MDT&w0E zOq=tQx|c7-XnBizr!geVNG=Fuz1@`5gNKSW!?{vS=*BOezF5^ON=jfo*tee&+zpSs z0Uf6y$vk{Yaq*%N)X!^e0cvD&nRv_>B|Heup!7S^TIm5%oEeo5b90nk{B)JclLk|m zp1+|!5{4~a6U{f`Gx_Eufo-db+xC5rvI90*L<*9FA--ix4lQ8 z9|2;e!Vj(3{@oQbAh(4Sb0b7`mGgV3NRbTxTAMU}6q~rf|%gu^*^A(~&s!v}CP3 z6~%F56Chl#blhnBB!o+{v*FHOG=AkY7^fU5R$4QA0uhsjo-65nRcykFvLG@B*|6Yw zHEFbE;iHfDp}YavYW6u|nZHcLR&oD>D)z&~$V_D<{8LVw<}G%DJiI21m%?9%Scfvr zkZPA#oi+`Mm3S|iosIvqPt(@)bwst%X{;LtBaRZ(l5*zS`#l`nwpC~1<89CMC|ZKa zBYl+Kd&XOQ|I3z@`^vv?$I&=ih7VrmI78G6L33{5>^O+ue3>Bjz(__A8dR2jB#nwM z^@vyg-Ufb`dfLm8+!g~pITJ8fNpjk`Bu$L%LFq+*Dy>_f4}F|59(OSV?4@o60%bT? zoB-YeP*+@(?$xIo&Mc_vebt)P9sV+o|MgBx3I?q$i@Up0?<4-{MGw7gzjk`9`=KNh zL@|uX%<{ccMZgujr9XY?gAqwpr`e>w1w#oW0ICfLj>4G%wnl$ z(8+3}q5!Fl^|ET2;IowtdkAdlkTb=K;~YiOc- z4aKgeRrj~(%|WMl1sq4s5%?e}gCDc9BG>wRvhiL$wKw_O3o1ui!a!Z6^`$3xhUU1- z-4)=>I8v58KKU4il;GP7fAJ{(L=#PdFAN{>eFUW144(}|sM@Q*r;#{NXpam!KEzBj z>pJ@6PzqvrpO;lYY861E@N{uSa`iSO zSP61-=mO~-)Y`jxN8sjn#iN7a{?kQC7wi#QN1IFy>-nCOTmSSF-*trUH{v8>?S9CG zCmfAQ(*UD^T9C+HMfr=MJ^@B~$>C$JpDz?FOfCMpoYmtMwP+pn)cPw2eND{_=?8O@ zRFWnFQOxszuCty&6SzXB=5Kd8=o{D7k9QmH{dXqOFZkOL4{H^zhMG@Jxo|Xad+C@( zf6Ig~4)s!_HWA?B;kfy{`b`?(wNyQ}#pdi#7qkEb%gC94mubttCwo17At9~Fz}NY( zfMb$1+dIg)p6lJM`C3!v>NDJ1;Ga;~T%TzPJYCd^wXE?aMuT!MFdE#^J&gF| zMEQ6$y0MNqgLo}eiD$Q>ru$2t&8BlwP0ihXLZ#>a=%QS%<4>LN1|>d~7RA4$OuX4S zba(@$Z;)0+&FRi*2XZ4q=8kc`{_T#LqH8lt&GHf>C|eLOR^O!x6O)VAG3bT&An0wX z8TgqM8kaL(a(ysyB0Bdx-dxYoQfY{77QPND{`a{)ybt2vxESEJDHicma$X)ZQ)O>O zdZ07eRXrSu5{$*}s9}xamOw=PgE?(~328HIWI*46&GUsEOb8 zF*PT#g&Vx#Bf^~W)#A26g7Or2kCVI6uZ?4 zDp$N-Y}mkS9LbJ(e9__JVYE?+<+JU3O!Qnh^6t^|N~Cbejp8m5HN2!@L_Cc87gi+rFN5zj zPk6`D&coGj1l;Sxp>~J-l|$;v(vbJ@%HkvA;YT;6b`z)Aoy^6_-VQx73p10o$Q?S9 zsQwV>(Z<=&DWm{~0YmcaX~wP)?85_!eXJLz>axI&frtY~aZx_UIFa%{&yJSJ6RsC- zm_Lfc2(`K<@tdFh2ACxmTjwB|#(UU7mawSKD$AI_sP)qk*B=!x|50(QjnQy6Rh>lY`0J_NNFSXW*T!=r zJXcohcd2!<-aFhv=c3)ys7Al_*$dcjUqC-U4+vD#X_wN3-P`;ltIj&*J7zfKq58s# zAxGf_a*Q>_6-OPc9>6YO4lgcEIZc<$sTQ<`y3|ts3Goo^!@G|a^sL_EzmzgeHZ=|# zV!#=3jE{0bw`IU=;Jht;YsM*B;)J!rJ`?5of5^fU{yuomYWmUI+4LpSHTv}nH&Y9b_hqL^tCHQLgQh%dG{P_~~&4^p+*GIQ2`YYemzEPzd zLl2|ls%Z^@lb2?(2t=4$I-#AYGisG}q+!C6iuw zIt68{ypGZP)fuuCkMGE6m-Vc76Hg?v2j{osdlWcTpgK23gFsXS`MxG%C)c57!v~cT z6YMLhH^MZrMc4Ep8WC79K&YPFj>S+1zc_a35Zc-w-T7yZZrDxHE`z%^?{sG#Uj*PD z%^EN7THK$97F!2pygFvf9*J5CS`oq z?Lz@)ZU=L}fGzKFMbN*%;Y@i%=ECn&rvEQ|{-i?b^`k4!(SRT(~?#a?$371o%v(R{^Pm&hCb0GwM`0&Zzbd4XeLu*LP#WBS#DLx`u+7wgi* zvp0!Pq%c=SWw8?ti|VSwES!Wul1eTf|H3R$gMTOd_IVRf7Nqz2n1gWm4;sg(Y8)oq zROvI;w|i5iiTJkSK4!QBIAj%XF4#s38a9}PXO|& z^&HhG8(5|yv;p2h=Haf$p<$xfhk}ptYSwWA1;l9R!L}n>WW+~>{h7htaw_m!+YqqT zlZFEK)-R+vA$?8T66o5rU=5r}@tn+B@e572#^6XgndX6XLo+ijMvNb}&t`l5erkjB zoK4UEMwRcq%{ib!E$+j7B0ta4x?yNYrs>E}xos6>No={S@Dt)*#u{DpABKGrw*@_Tj%{Vpz!#w|A)D0h5NB-ao+8 zW&Smib{z_eYC%qi0@bbjM*8 zrz1y_d6%EZgEAcWp1q>zbaHtINMPY#X!3g_NGNJ+0=}3?=5pI<57?~%{yMg)!iAt; z;+E@`qMLn&>mm=xGZPhS6`;C`yVw-z#B7Y)oX!Q)hLvI zuEnac#YrL$P-TrpNO6W-65U9=3zLMA#mXH-8aSu}@HyFt`pZl{S%C7@u=m}N zCgGV=h&i7Aa#dNxj)Mx~PP77nDR!PIgrF6bA94_S9RD(Pxi6jhiM;$cnX96iX_}qv zL}l~AT9;y6p~3!_RfWd?g!eg`oc9b4D1&c&UkHPS2ww^+ki9OJl;7Yt813$S0vrJb zpE_^%U?54Q3UH|=5-XFmRdcb68v;NKb3z&-EJpdM1l?|q*8|}9sFcxpl<3@GYkAwW zKddS)7PCfzFLh2zw;qzus;hg>W4)=Tp2ea(s7L(A{Xtv7pu78haDxZ;<^1wbC33IE z4FEB)307jF`@;}&)mT?>O0sqJ`%Z9xl7Trvb5s*`JzbfZtz-56Q_&oRCdX*%6*n{# zDeqps4Oyh~7r{0^Jg6}LEns8!#T!VIlq;#3i0chMlL*qM&PRUQ^G_(lI4{fARIyWd zm72=qBio|yQ>#02nc<@sVHt7|=2FL} zTwG8QZTiTxjBhM?R&R&6TrlG0IpRSu99{9r1hpZ|wX>mm@x0uvQS6dGe}dRKQhjqv zVWea^J2~fBTOK24IkN}tNnWFW(mh7AT(&t@5Z`}pbmcehJu5u>5(>$MAv1g4UStME zcanQms1^svNBtYwr^DA=jvFtgtp4keJaTlpUB{B`8|ux>RHcsy1#qNAZa;AvIfy|U zbs87jxKL1W3kbKY8g9QkwgI@P>KJ?r@13tu z=tKY-=-w*)9F$i@>tnlV@Wgx)u6agZ&^%(B$9&_qg&^a&`GDC45YiW} zf(4W|w@chI2=ZlaC{`J!0&-|`$*HFss98i65{bi$kak(cj}4)n|y@rRg$=LNceJiC5Am74fJi-1&$>eSITS^tl z&KSIQ;xmF<`o%>8xA%ZW-xV`y4SEnvJ+?Clo!ZHH!PgvE^320l}5|dS7Xyp%4p2 zf>Yoaf!^n^u`6vCbdP}~xvf^?dQC@w-^xIm@~9$nG{Pm?sv1^RnfKCX1tipk9449! zm;<%5CbOi3i8mHv5qd$2Ip+2JmN7JP83gpEf|FhlX_<3b4>PM3z2#>yH<(;`0kxWIv!?yUzF;ic_lqx`T#G`tx z10w`%rFNtg4{gRuvjb?-ECv;}R<*^JA-Fcno9dTe9kj-is5}3P8Z_FbH#}h&C|1Cu zqQQ_+7~5oD(_hwF2~|C!Zj@#@%d?wa47tLT|jE)>axU9!;z%qaPsC0f@ZzP9z& zse9u`!&35dYc1f93f>*f!K@#=E-p5cHqUP#!(v=n-I`5w&Nc5rp9jVI6u})?2(FWKI=aNdxu_(FcQ`mCTK*R z$t76|r`&ei^(bp;)6UG7F2ASLHng|3t&i-M^~|BRtjA`G|0!XFxbXBXS3Emb!KP5% zLERiL@<}dMHTh)RH1WX0bh`^1=V2NV_Q0Zur^7lcW%@ZIKeI=>s4( zswUrFu8_-0P0Q=^<_wLSB-+i`vr?Oj0vD1n1_f0v59jv7TH8H}b4m4y6`IaguT@=5 zTdm8@#u7C%m`3xa`qEzq1ZK|S;US~7XS${Fk0P&K8iazUa(!7!FNXA34XG(ENj;=q z|7WVJh0kT}uVzEe+J5y+>M>J=^W(NDYqPOkBkk5PEdx*HEE4sGZzuW)aISroc~b3J zTvHtz^V6c(`enS@g76OTSM4YqDcDCk$GGKZw|;{?3oLvOnDRwea_>sNvdOArwE5D9 z_W&03xq%|x!F~MfBf^gBV7<)=etm*Zq?kF4N%hJ@T~~s~LX&Q~DE--Wp5qdP^A6>1 zB>v#(LaErsbJV7*&yjCDg(FK7wox*8+&}YGrA^%0td|RB2$@7S%QrEnJr6DP8x3Oi4a&Ggb9oLp}qsBS$)H>H~jUEtv;nXv zqGFlnAn!%lTz}{LLx!oMG<+0YOjbJI`}&u^STFj!B6W==d{$+P{Y7QdX}>fprX_6r z4-24P&Uvbw_d)?2`tq$Y%jECGjF)sW|Mtw>0bYMmjZy1`Dc$6=ons1{EcGhR{;ieD z7n;X08M<`5;bdpXRNS*K!WAFoiUzE=6K2&WFu0n=wXXO9KBxjssq)oAeN}4c@z0It zoc=?egDuJ~X~ps>TAxNtYAjV5_2YT=zA_F2()G3CzyCk z==F3X%fW0a$p)^;dSPq{YA|XBI5gw?fORD~3}qnUM;o1pM3W{B#T2}y{~{RvtKk|* zmf&l0P;i$0K~^??rq}x}OL>-G5Y-AP2h9W4Hp{zutemtNDGJg4Hk0ux5ytO*ulGZYF0`XdzPp8 z);C1@0}Yf|q6#x$45C-uFjQCWQYWlrx2*xKc5y9A8z66C>GzVzakw+nB&sGuovikr zGXuNlN&SAYNIBY5K}cCBSkJlbq7n|xnW7Jd<0rufv-ly4Fb>baFiS24W@QgK4?=5V ztbO0;VP|_oMN=#IULon%K$zP*=zxEwC2y*ZULj!>m?SxYGRHch{7vXVq53&o{~7FH zxA12hfow+7{)Te>myf+Pf9gd`-BRErJ<^8_aV2iy%S^;9Mq(--D5jbM&ePv5TqA z=Bep9G{#f=;4uO5yECp)+Gxi;CAwSAv-u^Z^y`G`-=EVLc-lm>lCJFv~&og!K$vS0987aHF2hh%B zrGxD<#ICfWE*S10m#80u9I=2)XsK ztV#`e|caQ*3JhIFXmri>YipH zGoj)n7hD71wq0kBGXCxM6okuWPzk6J{{TLcg`r)P+5U#%gJy6O&k_Gb8iU?Z*>+7z zgCaxM2qmMivo~~0zp&=VJyvqU@eyO45thd^eO(S|beD4^?(s75xid?t#O@`h-4rwF z1}^n&c8)_hd-!wjIs6-W^5cnTv*m9=%hPkz@Vu|Q_X#v|wn5qs1~zATe%$tK1F#{e zcgJm>4NIna(S=c}SDf4yyzhc1oKwLM?xV-@0*ER>AbLKV$fpQRtAQ-f(#A|BuVx18 zDpkn)L7faAxoVs#RT0+SN}P?~qEYJ!)LJW5rvJ(n+u&F`DMQ*{GEvHi!@lf?7q)%e zce}U&I*>7{TC+qhI~pmTJ(jqKF+*dfnkFjtQQA+&mft5+%zj&v z8!cxvwr>gaYUeDS;nxFM+cT>!7z-w5gn40|yWVZpr>W4PxL$15W{NFSGNVWF7-niy z$I=K!;_`Yr@Q%{A{rua02KLa|TcS{6*qXePL|~-WOBbUtwM}yA*AxAeAUg`4`mXf5 zMR$Ts`riGj9Tqsxm78dJ4%Vun=1#63`w1&pma%`>x#aX@@;fe}-)Sr6E9|YWqb_^t zk!mF}Er>UguwzVTd0bk9&rLDanQ-UUE`Z)(jlD}Txv9oLj%#to58vWa`n(Qx{P;WN^xx#BDY<4 zLO>1fagcWvOBv&-SIgN;!<`L^0kdkyIWjadcSK6l5oEaUlKzlM(skujQ8PG@x`)Wr z&Pqrl3>8XrCy2)QRhMxyN}#kY_P#ZB?3Tum_n(PFJsoT)AsEriH_aVQirOM>iDYi% z3e63MKhU_QrycP5q|s-lepQ&Tper2#k~k@?;yXM>w{d+>WTy? zniNbi^xBZ_LGX#YJX7k0hdrqK18}|?6leajv6bVM74N59N4YKH+PC^Ar~?znAZa7% zmac>+Z(HbB#W}wyJV4Y!_u44(!KcL%zBJ))wT{#pmpgES&csi&KQ%aLAh@dxCtDQ} z@<&dSg80&s7hgK|-DI0{_YtTm59Myk7LiiKiM7{O@A#hi5k#3;O+%->zJQvSi*AC% zI}#QN@`DYVBxvPxflomBe3LFi*(LF#)|0>0$#~j0@x3{EEKddhE>9aE6zZJ|u_D!< z)*)Z$6gjhI=xjos8dErt!t;h^1ZCv@t@Y9`-J6UzU(MB~rF0mo7HsmdLczot;w=N8 z_UH>i3Kdvcm6Pj%`taiX(hL81k|6cHFDdGHftnbB_A@ET(&PjRLb8eQgr1tO({**x z3k^P!aH3SMzLv8LaY1XkDcyUn^tkHry8(%*Z+9UL35d(T{kZu~6#hRSQ%hVbf8#nc zf6ND$#3=eW)&}Z<<;~SCQ$x^1!D`^I9s{Mn23Y)n$^`EwD!J2{PwrA z{aR$wA$DgaW4uuxot(ZoiZYeftdU~Xx_VYf%S3fRlJ@ZL^ix?ieA`^!p#EVJwS?>n ze!wDWY7iyBRmkE}&$96<)rcDWy!rwrY~5M;I9rgA&}w2Tk(wIa>Zl8I=AUfH&^*XK zgnZL))P6aWL&xJA|4kpEUnLb+$s%a~p5Z=GT(E7lqy0mzPs;gmT*GJ3M`OBps_~+t ztN#S+>XC0H(1Vhf?L0&Gvh&#TJc`dNopChqe}0Nosf{=Wx&x$MR%UC7hAw@41J(%2 z?+4k1cXHQO@Y0U0;?>LkQX$+kXPLj4TpqK$SY%A1dtOyL%UWR#yv27`?YjhLoaPOV zv>Sm7n}S^V~F5Bd&KLO-Arvx~tFIf|jI2nJsYbM%IgBlfZBFYYH(S;3;O^hKFr1 z$Vl{ai;%0AcSTcU1{!{+LRFEhh8V#^yKhE&ZG}zm=SV;LY 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%0DAt036a2z-0yb#DnOKL;7GnZON_;zdo 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 Date: Wed, 5 Jun 2024 19:52:15 +0900 Subject: [PATCH 051/102] chore: fix struct name in comment (#1938) Signed-off-by: xiaoxiangirl --- x/ccv/types/expected_keepers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ccv/types/expected_keepers.go b/x/ccv/types/expected_keepers.go index b3815b6d65..77ac40eecf 100644 --- a/x/ccv/types/expected_keepers.go +++ b/x/ccv/types/expected_keepers.go @@ -143,7 +143,7 @@ type IBCTransferKeeper interface { Transfer(context.Context, *transfertypes.MsgTransfer) (*transfertypes.MsgTransferResponse, error) } -// IBCKeeper defines the expected interface needed for opening a +// IBCCoreKeeper defines the expected interface needed for opening a // channel type IBCCoreKeeper interface { ChannelOpenInit( From 46ad8356d5c6aabaa063c6a7f7d7cc0f07c2c4f7 Mon Sep 17 00:00:00 2001 From: insumity Date: Wed, 5 Jun 2024 13:20:25 +0200 Subject: [PATCH 052/102] feat!: allow consumer chains to change their PSS parameters (#1932) * added modification proposal * small fixes * Update x/ccv/provider/client/proposal_handler.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * removed trailing comma * Update x/ccv/provider/types/proposal.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * Update x/ccv/provider/types/proposal.go Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * took into account comment --------- Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../ccv/provider/v1/provider.proto | 33 +- x/ccv/provider/client/proposal_handler.go | 100 ++- x/ccv/provider/keeper/proposal.go | 33 + x/ccv/provider/keeper/proposal_test.go | 49 ++ x/ccv/provider/proposal_handler.go | 2 + x/ccv/provider/types/codec.go | 4 + x/ccv/provider/types/errors.go | 43 +- x/ccv/provider/types/proposal.go | 83 +- x/ccv/provider/types/proposal_test.go | 142 ++++ x/ccv/provider/types/provider.pb.go | 795 ++++++++++++++---- 10 files changed, 1103 insertions(+), 181 deletions(-) diff --git a/proto/interchain_security/ccv/provider/v1/provider.proto b/proto/interchain_security/ccv/provider/v1/provider.proto index 139cc9d25f..2a25eb533f 100644 --- a/proto/interchain_security/ccv/provider/v1/provider.proto +++ b/proto/interchain_security/ccv/provider/v1/provider.proto @@ -123,6 +123,37 @@ message ConsumerRemovalProposal { [ (gogoproto.stdtime) = true, (gogoproto.nullable) = false ]; } +// ConsumerModificationProposal is a governance proposal on the provider chain to modify parameters of a running +// consumer chain. If it passes, the consumer chain's state is updated to take into account the newest params. +message ConsumerModificationProposal { + // the title of the proposal + string title = 1; + // the description of the proposal + string description = 2; + // the chain-id of the consumer chain to be modified + string chain_id = 3; + // Corresponds to the percentage of validators that have to validate the chain under the Top N case. + // For example, 53 corresponds to a Top 53% chain, meaning that the top 53% provider validators by voting power + // have to validate the proposed consumer chain. top_N can either be 0 or any value in [50, 100]. + // A chain can join with top_N == 0 as an Opt In chain, or with top_N ∈ [50, 100] as a Top N chain. + uint32 top_N = 4; + // Corresponds to the maximum power (percentage-wise) a validator can have on the consumer chain. For instance, if + // `validators_power_cap` is set to 32, it means that no validator can have more than 32% of the voting power on the + // consumer chain. Note that this might not be feasible. For example, think of a consumer chain with only + // 5 validators and with `validators_power_cap` set to 10%. In such a scenario, at least one validator would need + // to have more than 20% of the total voting power. Therefore, `validators_power_cap` operates on a best-effort basis. + uint32 validators_power_cap = 5; + // Corresponds to the maximum number of validators that can validate a consumer chain. + // Only applicable to Opt In chains. Setting `validator_set_cap` on a Top N chain is a no-op. + uint32 validator_set_cap = 6; + // Corresponds to a list of provider consensus addresses of validators that are the ONLY ones that can validate + // the consumer chain. + repeated string allowlist = 7; + // Corresponds to a list of provider consensus addresses of validators that CANNOT validate the consumer chain. + repeated string denylist = 8; +} + + // EquivocationProposal is a governance proposal on the provider chain to // punish a validator for equivocation on a consumer chain. // @@ -133,7 +164,7 @@ message EquivocationProposal { option deprecated = true; // the title of the proposal string title = 1; -// the description of the proposal + // the description of the proposal string description = 2; // the list of equivocations that will be processed repeated cosmos.evidence.v1beta1.Equivocation equivocations = 3; diff --git a/x/ccv/provider/client/proposal_handler.go b/x/ccv/provider/client/proposal_handler.go index 022d131e4d..2de87504ad 100644 --- a/x/ccv/provider/client/proposal_handler.go +++ b/x/ccv/provider/client/proposal_handler.go @@ -24,9 +24,10 @@ import ( ) var ( - ConsumerAdditionProposalHandler = govclient.NewProposalHandler(SubmitConsumerAdditionPropTxCmd) - ConsumerRemovalProposalHandler = govclient.NewProposalHandler(SubmitConsumerRemovalProposalTxCmd) - ChangeRewardDenomsProposalHandler = govclient.NewProposalHandler(SubmitChangeRewardDenomsProposalTxCmd) + ConsumerAdditionProposalHandler = govclient.NewProposalHandler(SubmitConsumerAdditionPropTxCmd) + ConsumerRemovalProposalHandler = govclient.NewProposalHandler(SubmitConsumerRemovalProposalTxCmd) + ConsumerModificationProposalHandler = govclient.NewProposalHandler(SubmitConsumerModificationProposalTxCmd) + ChangeRewardDenomsProposalHandler = govclient.NewProposalHandler(SubmitChangeRewardDenomsProposalTxCmd) ) // SubmitConsumerAdditionPropTxCmd returns a CLI command handler for submitting @@ -172,6 +173,70 @@ Where proposal.json contains: } } +// SubmitConsumerModificationProposalTxCmd returns a CLI command handler for submitting +// a consumer modification proposal via a transaction. +func SubmitConsumerModificationProposalTxCmd() *cobra.Command { + return &cobra.Command{ + Use: "consumer-modification [proposal-file]", + Args: cobra.ExactArgs(1), + Short: "Submit a consumer modification proposal", + Long: ` +Submit a consumer modification proposal along with an initial deposit. +The proposal details must be supplied via a JSON file. + +Example: +$ tx gov submit-legacy-proposal consumer-modification --from= + +Where proposal.json contains: + +{ + "title": "Modify FooChain", + "summary": "Make it an Opt In chain", + "chain_id": "foochain", + "top_n": 0, + "validators_power_cap": 32, + "validator_set_cap": 50, + "allowlist": [], + "denylist": ["validatorAConsensusAddress", "validatorBConsensusAddress"] +} + `, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + proposal, err := ParseConsumerModificationProposalJSON(args[0]) + if err != nil { + return err + } + + content := types.NewConsumerModificationProposal( + proposal.Title, proposal.Summary, proposal.ChainId, proposal.TopN, + proposal.ValidatorsPowerCap, proposal.ValidatorSetCap, proposal.Allowlist, proposal.Denylist) + + from := clientCtx.GetFromAddress() + + deposit, err := sdk.ParseCoinsNormalized(proposal.Deposit) + if err != nil { + return err + } + + msgContent, err := govv1.NewLegacyContent(content, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + if err != nil { + return err + } + + msg, err := govv1.NewMsgSubmitProposal([]sdk.Msg{msgContent}, deposit, from.String(), "", content.GetTitle(), proposal.Summary) + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } +} + // SubmitChangeRewardDenomsProposalTxCmd returns a CLI command handler for submitting // a change reward denoms proposal via a transaction. func SubmitChangeRewardDenomsProposalTxCmd() *cobra.Command { @@ -326,6 +391,35 @@ func ParseConsumerRemovalProposalJSON(proposalFile string) (ConsumerRemovalPropo return proposal, nil } +type ConsumerModificationProposalJSON struct { + Title string `json:"title"` + Summary string `json:"summary"` + ChainId string `json:"chain_id"` + + TopN uint32 `json:"top_N"` + ValidatorsPowerCap uint32 `json:"validators_power_cap"` + ValidatorSetCap uint32 `json:"validator_set_cap"` + Allowlist []string `json:"allowlist"` + Denylist []string `json:"denylist"` + + Deposit string `json:"deposit"` +} + +func ParseConsumerModificationProposalJSON(proposalFile string) (ConsumerModificationProposalJSON, error) { + proposal := ConsumerModificationProposalJSON{} + + contents, err := os.ReadFile(filepath.Clean(proposalFile)) + if err != nil { + return proposal, err + } + + if err := json.Unmarshal(contents, &proposal); err != nil { + return proposal, err + } + + return proposal, nil +} + type ChangeRewardDenomsProposalJSON struct { Summary string `json:"summary"` types.ChangeRewardDenomsProposal diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index e112e6b761..15281c797b 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -144,6 +144,39 @@ func (k Keeper) HandleConsumerRemovalProposal(ctx sdk.Context, p *types.Consumer return nil } +// HandleConsumerModificationProposal modifies a running consumer chain +func (k Keeper) HandleConsumerModificationProposal(ctx sdk.Context, p *types.ConsumerModificationProposal) error { + if _, found := k.GetConsumerClientId(ctx, p.ChainId); !found { + return fmt.Errorf("consumer chain (%s) is not runnig", p.ChainId) + } + + k.SetTopN(ctx, p.ChainId, p.Top_N) + k.SetValidatorsPowerCap(ctx, p.ChainId, p.ValidatorsPowerCap) + k.SetValidatorSetCap(ctx, p.ChainId, p.ValidatorSetCap) + + k.DeleteAllowlist(ctx, p.ChainId) + for _, address := range p.Allowlist { + consAddr, err := sdk.ConsAddressFromBech32(address) + if err != nil { + continue + } + + k.SetAllowlist(ctx, p.ChainId, types.NewProviderConsAddress(consAddr)) + } + + k.DeleteDenylist(ctx, p.ChainId) + for _, address := range p.Denylist { + consAddr, err := sdk.ConsAddressFromBech32(address) + if err != nil { + continue + } + + k.SetDenylist(ctx, p.ChainId, types.NewProviderConsAddress(consAddr)) + } + + return nil +} + // StopConsumerChain cleans up the states for the given consumer chain ID and // completes the outstanding unbonding operations on the consumer chain. // diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index 84cb91acb8..c561960a49 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -517,6 +517,55 @@ func TestHandleConsumerRemovalProposal(t *testing.T) { } } +func TestHandleConsumerModificationProposal(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainID := "chainID" + + // set up a consumer client, so it seems that "chainID" is running + providerKeeper.SetConsumerClientId(ctx, "chainID", "clientID") + + // set PSS-related fields to update them later on + providerKeeper.SetTopN(ctx, chainID, 50) + providerKeeper.SetValidatorSetCap(ctx, chainID, 10) + providerKeeper.SetValidatorsPowerCap(ctx, chainID, 34) + providerKeeper.SetAllowlist(ctx, chainID, providertypes.NewProviderConsAddress([]byte("allowlistedAddr1"))) + providerKeeper.SetAllowlist(ctx, chainID, providertypes.NewProviderConsAddress([]byte("allowlistedAddr2"))) + providerKeeper.SetDenylist(ctx, chainID, providertypes.NewProviderConsAddress([]byte("denylistedAddr1"))) + + expectedTopN := uint32(75) + expectedValidatorsPowerCap := uint32(67) + expectedValidatorSetCap := uint32(20) + expectedAllowlistedValidator := "cosmosvalcons1wpex7anfv3jhystyv3eq20r35a" + expectedDenylistedValidator := "cosmosvalcons1nx7n5uh0ztxsynn4sje6eyq2ud6rc6klc96w39" + proposal := providertypes.NewConsumerModificationProposal("title", "description", chainID, + expectedTopN, + expectedValidatorsPowerCap, + expectedValidatorSetCap, + []string{expectedAllowlistedValidator}, + []string{expectedDenylistedValidator}, + ).(*providertypes.ConsumerModificationProposal) + + err := providerKeeper.HandleConsumerModificationProposal(ctx, proposal) + require.NoError(t, err) + + actualTopN, _ := providerKeeper.GetTopN(ctx, chainID) + require.Equal(t, expectedTopN, actualTopN) + actualValidatorsPowerCap, _ := providerKeeper.GetValidatorsPowerCap(ctx, chainID) + require.Equal(t, expectedValidatorsPowerCap, actualValidatorsPowerCap) + actualValidatorSetCap, _ := providerKeeper.GetValidatorSetCap(ctx, chainID) + require.Equal(t, expectedValidatorSetCap, actualValidatorSetCap) + + allowlistedValidator, err := sdk.ConsAddressFromBech32(expectedAllowlistedValidator) + require.Equal(t, 1, len(providerKeeper.GetAllowList(ctx, chainID))) + require.Equal(t, providertypes.NewProviderConsAddress(allowlistedValidator), providerKeeper.GetAllowList(ctx, chainID)[0]) + + denylistedValidator, err := sdk.ConsAddressFromBech32(expectedDenylistedValidator) + require.Equal(t, 1, len(providerKeeper.GetDenyList(ctx, chainID))) + require.Equal(t, providertypes.NewProviderConsAddress(denylistedValidator), providerKeeper.GetDenyList(ctx, chainID)[0]) +} + // Tests the StopConsumerChain method against the spec, // with more granularity than what's covered in TestHandleConsumerRemovalProposal, or integration tests. // See: https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/methods.md#ccv-pcf-stcc1 diff --git a/x/ccv/provider/proposal_handler.go b/x/ccv/provider/proposal_handler.go index 50089a8ab5..98e349652f 100644 --- a/x/ccv/provider/proposal_handler.go +++ b/x/ccv/provider/proposal_handler.go @@ -21,6 +21,8 @@ func NewProviderProposalHandler(k keeper.Keeper) govv1beta1.Handler { return k.HandleConsumerAdditionProposal(ctx, c) case *types.ConsumerRemovalProposal: return k.HandleConsumerRemovalProposal(ctx, c) + case *types.ConsumerModificationProposal: + return k.HandleConsumerModificationProposal(ctx, c) case *types.ChangeRewardDenomsProposal: return k.HandleConsumerRewardDenomProposal(ctx, c) default: diff --git a/x/ccv/provider/types/codec.go b/x/ccv/provider/types/codec.go index cc842276fb..d817de9302 100644 --- a/x/ccv/provider/types/codec.go +++ b/x/ccv/provider/types/codec.go @@ -26,6 +26,10 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { (*govv1beta1.Content)(nil), &ConsumerRemovalProposal{}, ) + registry.RegisterImplementations( + (*govv1beta1.Content)(nil), + &ConsumerModificationProposal{}, + ) registry.RegisterImplementations( (*sdk.Msg)(nil), &MsgAssignConsumerKey{}, diff --git a/x/ccv/provider/types/errors.go b/x/ccv/provider/types/errors.go index 32000ed65f..93e88397c4 100644 --- a/x/ccv/provider/types/errors.go +++ b/x/ccv/provider/types/errors.go @@ -6,25 +6,26 @@ import ( // Provider sentinel errors var ( - ErrInvalidConsumerAdditionProposal = errorsmod.Register(ModuleName, 1, "invalid consumer addition proposal") - ErrInvalidConsumerRemovalProp = errorsmod.Register(ModuleName, 2, "invalid consumer removal proposal") - ErrUnknownConsumerChainId = errorsmod.Register(ModuleName, 3, "no consumer chain with this chain id") - ErrUnknownConsumerChannelId = errorsmod.Register(ModuleName, 4, "no consumer chain with this channel id") - ErrInvalidConsumerConsensusPubKey = errorsmod.Register(ModuleName, 5, "empty consumer consensus public key") - ErrInvalidConsumerChainID = errorsmod.Register(ModuleName, 6, "invalid consumer chain id") - ErrConsumerKeyNotFound = errorsmod.Register(ModuleName, 7, "consumer key not found") - ErrNoValidatorConsumerAddress = errorsmod.Register(ModuleName, 8, "error getting validator consumer address") - ErrNoValidatorProviderAddress = errorsmod.Register(ModuleName, 9, "error getting validator provider address") - ErrConsumerKeyInUse = errorsmod.Register(ModuleName, 10, "consumer key is already in use by a validator") - ErrCannotAssignDefaultKeyAssignment = errorsmod.Register(ModuleName, 11, "cannot re-assign default key assignment") - ErrInvalidConsumerParams = errorsmod.Register(ModuleName, 12, "invalid consumer params") - ErrInvalidProviderAddress = errorsmod.Register(ModuleName, 13, "invalid provider address") - ErrInvalidConsumerRewardDenom = errorsmod.Register(ModuleName, 14, "invalid consumer reward denom") - ErrInvalidDepositorAddress = errorsmod.Register(ModuleName, 15, "invalid depositor address") - ErrInvalidConsumerClient = errorsmod.Register(ModuleName, 16, "ccv channel is not built on correct client") - ErrDuplicateConsumerChain = errorsmod.Register(ModuleName, 17, "consumer chain already exists") - ErrConsumerChainNotFound = errorsmod.Register(ModuleName, 18, "consumer chain not found") - ErrInvalidConsumerCommissionRate = errorsmod.Register(ModuleName, 19, "consumer commission rate is invalid") - ErrCannotOptOutFromTopN = errorsmod.Register(ModuleName, 20, "cannot opt out from a Top N chain") - ErrNoUnconfirmedVSCPacket = errorsmod.Register(ModuleName, 21, "no unconfirmed vsc packet for this chain id") + ErrInvalidConsumerAdditionProposal = errorsmod.Register(ModuleName, 1, "invalid consumer addition proposal") + ErrInvalidConsumerRemovalProp = errorsmod.Register(ModuleName, 2, "invalid consumer removal proposal") + ErrUnknownConsumerChainId = errorsmod.Register(ModuleName, 3, "no consumer chain with this chain id") + ErrUnknownConsumerChannelId = errorsmod.Register(ModuleName, 4, "no consumer chain with this channel id") + ErrInvalidConsumerConsensusPubKey = errorsmod.Register(ModuleName, 5, "empty consumer consensus public key") + ErrInvalidConsumerChainID = errorsmod.Register(ModuleName, 6, "invalid consumer chain id") + ErrConsumerKeyNotFound = errorsmod.Register(ModuleName, 7, "consumer key not found") + ErrNoValidatorConsumerAddress = errorsmod.Register(ModuleName, 8, "error getting validator consumer address") + ErrNoValidatorProviderAddress = errorsmod.Register(ModuleName, 9, "error getting validator provider address") + ErrConsumerKeyInUse = errorsmod.Register(ModuleName, 10, "consumer key is already in use by a validator") + ErrCannotAssignDefaultKeyAssignment = errorsmod.Register(ModuleName, 11, "cannot re-assign default key assignment") + ErrInvalidConsumerParams = errorsmod.Register(ModuleName, 12, "invalid consumer params") + ErrInvalidProviderAddress = errorsmod.Register(ModuleName, 13, "invalid provider address") + ErrInvalidConsumerRewardDenom = errorsmod.Register(ModuleName, 14, "invalid consumer reward denom") + ErrInvalidDepositorAddress = errorsmod.Register(ModuleName, 15, "invalid depositor address") + ErrInvalidConsumerClient = errorsmod.Register(ModuleName, 16, "ccv channel is not built on correct client") + ErrDuplicateConsumerChain = errorsmod.Register(ModuleName, 17, "consumer chain already exists") + ErrConsumerChainNotFound = errorsmod.Register(ModuleName, 18, "consumer chain not found") + ErrInvalidConsumerCommissionRate = errorsmod.Register(ModuleName, 19, "consumer commission rate is invalid") + ErrCannotOptOutFromTopN = errorsmod.Register(ModuleName, 20, "cannot opt out from a Top N chain") + ErrNoUnconfirmedVSCPacket = errorsmod.Register(ModuleName, 21, "no unconfirmed vsc packet for this chain id") + ErrInvalidConsumerModificationProposal = errorsmod.Register(ModuleName, 22, "invalid consumer modification proposal") ) diff --git a/x/ccv/provider/types/proposal.go b/x/ccv/provider/types/proposal.go index fa6483acd1..0e42f2a3ab 100644 --- a/x/ccv/provider/types/proposal.go +++ b/x/ccv/provider/types/proposal.go @@ -18,15 +18,17 @@ import ( ) const ( - ProposalTypeConsumerAddition = "ConsumerAddition" - ProposalTypeConsumerRemoval = "ConsumerRemoval" - ProposalTypeEquivocation = "Equivocation" - ProposalTypeChangeRewardDenoms = "ChangeRewardDenoms" + ProposalTypeConsumerAddition = "ConsumerAddition" + ProposalTypeConsumerRemoval = "ConsumerRemoval" + ProposalTypeConsumerModification = "ConsumerModification" + ProposalTypeEquivocation = "Equivocation" + ProposalTypeChangeRewardDenoms = "ChangeRewardDenoms" ) var ( _ govv1beta1.Content = &ConsumerAdditionProposal{} _ govv1beta1.Content = &ConsumerRemovalProposal{} + _ govv1beta1.Content = &ConsumerModificationProposal{} _ govv1beta1.Content = &ChangeRewardDenomsProposal{} _ govv1beta1.Content = &EquivocationProposal{} ) @@ -34,6 +36,7 @@ var ( func init() { govv1beta1.RegisterProposalType(ProposalTypeConsumerAddition) govv1beta1.RegisterProposalType(ProposalTypeConsumerRemoval) + govv1beta1.RegisterProposalType(ProposalTypeConsumerModification) govv1beta1.RegisterProposalType(ProposalTypeChangeRewardDenoms) govv1beta1.RegisterProposalType(ProposalTypeEquivocation) } @@ -92,6 +95,21 @@ func (cccp *ConsumerAdditionProposal) ProposalType() string { return ProposalTypeConsumerAddition } +// ValidatePSSFeatures returns an error if the `topN` and `validatorsPowerCap` parameters are no in the correct ranges +func ValidatePSSFeatures(topN uint32, validatorsPowerCap uint32) error { + // Top N corresponds to the top N% of validators that have to validate the consumer chain and can only be 0 (for an + // Opt In chain) or in the range [50, 100] (for a Top N chain). + if topN != 0 && (topN < 50 || topN > 100) { + return fmt.Errorf("Top N can either be 0 or in the range [50, 100]") + } + + if validatorsPowerCap != 0 && validatorsPowerCap > 100 { + return fmt.Errorf("validators' power cap has to be in the range [1, 100]") + } + + return nil +} + // ValidateBasic runs basic stateless validity checks func (cccp *ConsumerAdditionProposal) ValidateBasic() error { if err := govv1beta1.ValidateAbstract(cccp); err != nil { @@ -145,16 +163,10 @@ func (cccp *ConsumerAdditionProposal) ValidateBasic() error { return errorsmod.Wrap(ErrInvalidConsumerAdditionProposal, "unbonding period cannot be zero") } - // Top N corresponds to the top N% of validators that have to validate the consumer chain and can only be 0 (for an - // Opt In chain) or in the range [50, 100] (for a Top N chain). - if cccp.Top_N != 0 && (cccp.Top_N < 50 || cccp.Top_N > 100) { - return errorsmod.Wrap(ErrInvalidConsumerAdditionProposal, "Top N can either be 0 or in the range [50, 100]") - } - - if cccp.ValidatorsPowerCap != 0 && cccp.ValidatorSetCap > 100 { - return errorsmod.Wrap(ErrInvalidConsumerAdditionProposal, "validators' power cap has to be in the range [1, 100]") + err := ValidatePSSFeatures(cccp.Top_N, cccp.ValidatorsPowerCap) + if err != nil { + return errorsmod.Wrapf(ErrInvalidConsumerAdditionProposal, "invalid PSS features: %s", err.Error()) } - return nil } @@ -223,6 +235,51 @@ func (sccp *ConsumerRemovalProposal) ValidateBasic() error { return nil } +// NewConsumerModificationProposal creates a new consumer modificaton proposal. +func NewConsumerModificationProposal(title, description, chainID string, + topN uint32, + validatorsPowerCap uint32, + validatorSetCap uint32, + allowlist []string, + denylist []string, +) govv1beta1.Content { + return &ConsumerModificationProposal{ + Title: title, + Description: description, + ChainId: chainID, + Top_N: topN, + ValidatorsPowerCap: validatorsPowerCap, + ValidatorSetCap: validatorSetCap, + Allowlist: allowlist, + Denylist: denylist, + } +} + +// ProposalRoute returns the routing key of a consumer modification proposal. +func (cccp *ConsumerModificationProposal) ProposalRoute() string { return RouterKey } + +// ProposalType returns the type of the consumer modification proposal. +func (cccp *ConsumerModificationProposal) ProposalType() string { + return ProposalTypeConsumerModification +} + +// ValidateBasic runs basic stateless validity checks +func (cccp *ConsumerModificationProposal) ValidateBasic() error { + if err := govv1beta1.ValidateAbstract(cccp); err != nil { + return err + } + + if strings.TrimSpace(cccp.ChainId) == "" { + return errorsmod.Wrap(ErrInvalidConsumerModificationProposal, "consumer chain id must not be blank") + } + + err := ValidatePSSFeatures(cccp.Top_N, cccp.ValidatorsPowerCap) + if err != nil { + return errorsmod.Wrapf(ErrInvalidConsumerModificationProposal, "invalid PSS features: %s", err.Error()) + } + return nil +} + // NewEquivocationProposal creates a new equivocation proposal. // [DEPRECATED]: do not use because equivocations can be submitted // and verified automatically on the provider. diff --git a/x/ccv/provider/types/proposal_test.go b/x/ccv/provider/types/proposal_test.go index 465a376b47..91254ad265 100644 --- a/x/ccv/provider/types/proposal_test.go +++ b/x/ccv/provider/types/proposal_test.go @@ -297,6 +297,60 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { ), false, }, + { + "top N is invalid", + types.NewConsumerAdditionProposal("title", "description", "chainID", initialHeight, []byte("gen_hash"), []byte("bin_hash"), time.Now(), + "0.75", + 10, + "", + 10000, + 100000000000, + 100000000000, + 100000000000, + 10, + 0, + 0, + nil, + nil, + ), + false, + }, + { + "validators power cap is invalid", + types.NewConsumerAdditionProposal("title", "description", "chainID", initialHeight, []byte("gen_hash"), []byte("bin_hash"), time.Now(), + "0.75", + 10, + "", + 10000, + 100000000000, + 100000000000, + 100000000000, + 50, + 101, + 0, + nil, + nil, + ), + false, + }, + { + "valid proposal with PSS features", + types.NewConsumerAdditionProposal("title", "description", "chainID", initialHeight, []byte("gen_hash"), []byte("bin_hash"), time.Now(), + "0.75", + 10, + "", + 10000, + 100000000000, + 100000000000, + 100000000000, + 0, + 34, + 101, + []string{"addr1"}, + []string{"addr2", "addr3"}, + ), + true, + }, } for _, tc := range testCases { @@ -446,3 +500,91 @@ func TestChangeRewardDenomsProposalValidateBasic(t *testing.T) { }) } } + +func TestConsumerModificationProposalValidateBasic(t *testing.T) { + testCases := []struct { + name string + proposal govv1beta1.Content + expPass bool + }{ + { + "success", + types.NewConsumerModificationProposal("title", "description", "chainID", + 50, + 100, + 34, + []string{"addr1"}, + nil, + ), + true, + }, + { + "invalid chain id", + types.NewConsumerModificationProposal("title", "description", " ", + 0, + 0, + 0, + nil, + nil, + ), + false, + }, + { + "top N is invalid", + types.NewConsumerModificationProposal("title", "description", "chainID", + 10, + 0, + 0, + nil, + nil, + ), + false, + }, + { + "validators power cap is invalid", + types.NewConsumerModificationProposal("title", "description", "chainID", + 50, + 101, + 0, + nil, + nil, + ), + false, + }, + { + "valid proposal", + types.NewConsumerModificationProposal("title", "description", "chainID", + 0, + 34, + 101, + []string{"addr1"}, + []string{"addr2", "addr3"}, + ), + true, + }, + } + + for _, tc := range testCases { + + err := tc.proposal.ValidateBasic() + if tc.expPass { + require.NoError(t, err, "valid case: %s should not return error. got %w", tc.name, err) + } else { + require.Error(t, err, "invalid case: '%s' must return error but got none", tc.name) + } + } +} + +func TestValidatePSSFeatures(t *testing.T) { + require.NoError(t, types.ValidatePSSFeatures(0, 0)) + require.NoError(t, types.ValidatePSSFeatures(50, 0)) + require.NoError(t, types.ValidatePSSFeatures(100, 0)) + require.NoError(t, types.ValidatePSSFeatures(0, 10)) + require.NoError(t, types.ValidatePSSFeatures(0, 100)) + require.NoError(t, types.ValidatePSSFeatures(50, 100)) + + require.Error(t, types.ValidatePSSFeatures(10, 0)) + require.Error(t, types.ValidatePSSFeatures(49, 0)) + require.Error(t, types.ValidatePSSFeatures(101, 0)) + require.Error(t, types.ValidatePSSFeatures(50, 101)) +} diff --git a/x/ccv/provider/types/provider.pb.go b/x/ccv/provider/types/provider.pb.go index 4f0a2fd605..0540dfb0a6 100644 --- a/x/ccv/provider/types/provider.pb.go +++ b/x/ccv/provider/types/provider.pb.go @@ -222,6 +222,125 @@ func (m *ConsumerRemovalProposal) GetStopTime() time.Time { return time.Time{} } +// ConsumerModificationProposal is a governance proposal on the provider chain to modify parameters of a running +// consumer chain. If it passes, the consumer chain's state is updated to take into account the newest params. +type ConsumerModificationProposal struct { + // the title of the proposal + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + // the description of the proposal + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + // the chain-id of the consumer chain to be modified + ChainId string `protobuf:"bytes,3,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + // Corresponds to the percentage of validators that have to validate the chain under the Top N case. + // For example, 53 corresponds to a Top 53% chain, meaning that the top 53% provider validators by voting power + // have to validate the proposed consumer chain. top_N can either be 0 or any value in [50, 100]. + // A chain can join with top_N == 0 as an Opt In chain, or with top_N ∈ [50, 100] as a Top N chain. + Top_N uint32 `protobuf:"varint,4,opt,name=top_N,json=topN,proto3" json:"top_N,omitempty"` + // Corresponds to the maximum power (percentage-wise) a validator can have on the consumer chain. For instance, if + // `validators_power_cap` is set to 32, it means that no validator can have more than 32% of the voting power on the + // consumer chain. Note that this might not be feasible. For example, think of a consumer chain with only + // 5 validators and with `validators_power_cap` set to 10%. In such a scenario, at least one validator would need + // to have more than 20% of the total voting power. Therefore, `validators_power_cap` operates on a best-effort basis. + ValidatorsPowerCap uint32 `protobuf:"varint,5,opt,name=validators_power_cap,json=validatorsPowerCap,proto3" json:"validators_power_cap,omitempty"` + // Corresponds to the maximum number of validators that can validate a consumer chain. + // Only applicable to Opt In chains. Setting `validator_set_cap` on a Top N chain is a no-op. + ValidatorSetCap uint32 `protobuf:"varint,6,opt,name=validator_set_cap,json=validatorSetCap,proto3" json:"validator_set_cap,omitempty"` + // Corresponds to a list of provider consensus addresses of validators that are the ONLY ones that can validate + // the consumer chain. + Allowlist []string `protobuf:"bytes,7,rep,name=allowlist,proto3" json:"allowlist,omitempty"` + // Corresponds to a list of provider consensus addresses of validators that CANNOT validate the consumer chain. + Denylist []string `protobuf:"bytes,8,rep,name=denylist,proto3" json:"denylist,omitempty"` +} + +func (m *ConsumerModificationProposal) Reset() { *m = ConsumerModificationProposal{} } +func (m *ConsumerModificationProposal) String() string { return proto.CompactTextString(m) } +func (*ConsumerModificationProposal) ProtoMessage() {} +func (*ConsumerModificationProposal) Descriptor() ([]byte, []int) { + return fileDescriptor_f22ec409a72b7b72, []int{2} +} +func (m *ConsumerModificationProposal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConsumerModificationProposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConsumerModificationProposal.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ConsumerModificationProposal) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConsumerModificationProposal.Merge(m, src) +} +func (m *ConsumerModificationProposal) XXX_Size() int { + return m.Size() +} +func (m *ConsumerModificationProposal) XXX_DiscardUnknown() { + xxx_messageInfo_ConsumerModificationProposal.DiscardUnknown(m) +} + +var xxx_messageInfo_ConsumerModificationProposal proto.InternalMessageInfo + +func (m *ConsumerModificationProposal) GetTitle() string { + if m != nil { + return m.Title + } + return "" +} + +func (m *ConsumerModificationProposal) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func (m *ConsumerModificationProposal) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +func (m *ConsumerModificationProposal) GetTop_N() uint32 { + if m != nil { + return m.Top_N + } + return 0 +} + +func (m *ConsumerModificationProposal) GetValidatorsPowerCap() uint32 { + if m != nil { + return m.ValidatorsPowerCap + } + return 0 +} + +func (m *ConsumerModificationProposal) GetValidatorSetCap() uint32 { + if m != nil { + return m.ValidatorSetCap + } + return 0 +} + +func (m *ConsumerModificationProposal) GetAllowlist() []string { + if m != nil { + return m.Allowlist + } + return nil +} + +func (m *ConsumerModificationProposal) GetDenylist() []string { + if m != nil { + return m.Denylist + } + return nil +} + // EquivocationProposal is a governance proposal on the provider chain to // punish a validator for equivocation on a consumer chain. // @@ -243,7 +362,7 @@ func (m *EquivocationProposal) Reset() { *m = EquivocationProposal{} } func (m *EquivocationProposal) String() string { return proto.CompactTextString(m) } func (*EquivocationProposal) ProtoMessage() {} func (*EquivocationProposal) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{2} + return fileDescriptor_f22ec409a72b7b72, []int{3} } func (m *EquivocationProposal) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -310,7 +429,7 @@ func (m *ChangeRewardDenomsProposal) Reset() { *m = ChangeRewardDenomsPr func (m *ChangeRewardDenomsProposal) String() string { return proto.CompactTextString(m) } func (*ChangeRewardDenomsProposal) ProtoMessage() {} func (*ChangeRewardDenomsProposal) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{3} + return fileDescriptor_f22ec409a72b7b72, []int{4} } func (m *ChangeRewardDenomsProposal) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -391,7 +510,7 @@ func (m *GlobalSlashEntry) Reset() { *m = GlobalSlashEntry{} } func (m *GlobalSlashEntry) String() string { return proto.CompactTextString(m) } func (*GlobalSlashEntry) ProtoMessage() {} func (*GlobalSlashEntry) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{4} + return fileDescriptor_f22ec409a72b7b72, []int{5} } func (m *GlobalSlashEntry) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -480,7 +599,7 @@ func (m *Params) Reset() { *m = Params{} } func (m *Params) String() string { return proto.CompactTextString(m) } func (*Params) ProtoMessage() {} func (*Params) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{5} + return fileDescriptor_f22ec409a72b7b72, []int{6} } func (m *Params) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -582,7 +701,7 @@ func (m *SlashAcks) Reset() { *m = SlashAcks{} } func (m *SlashAcks) String() string { return proto.CompactTextString(m) } func (*SlashAcks) ProtoMessage() {} func (*SlashAcks) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{6} + return fileDescriptor_f22ec409a72b7b72, []int{7} } func (m *SlashAcks) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -629,7 +748,7 @@ func (m *ConsumerAdditionProposals) Reset() { *m = ConsumerAdditionPropo func (m *ConsumerAdditionProposals) String() string { return proto.CompactTextString(m) } func (*ConsumerAdditionProposals) ProtoMessage() {} func (*ConsumerAdditionProposals) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{7} + return fileDescriptor_f22ec409a72b7b72, []int{8} } func (m *ConsumerAdditionProposals) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -676,7 +795,7 @@ func (m *ConsumerRemovalProposals) Reset() { *m = ConsumerRemovalProposa func (m *ConsumerRemovalProposals) String() string { return proto.CompactTextString(m) } func (*ConsumerRemovalProposals) ProtoMessage() {} func (*ConsumerRemovalProposals) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{8} + return fileDescriptor_f22ec409a72b7b72, []int{9} } func (m *ConsumerRemovalProposals) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -721,7 +840,7 @@ func (m *AddressList) Reset() { *m = AddressList{} } func (m *AddressList) String() string { return proto.CompactTextString(m) } func (*AddressList) ProtoMessage() {} func (*AddressList) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{9} + return fileDescriptor_f22ec409a72b7b72, []int{10} } func (m *AddressList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -767,7 +886,7 @@ func (m *ChannelToChain) Reset() { *m = ChannelToChain{} } func (m *ChannelToChain) String() string { return proto.CompactTextString(m) } func (*ChannelToChain) ProtoMessage() {} func (*ChannelToChain) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{10} + return fileDescriptor_f22ec409a72b7b72, []int{11} } func (m *ChannelToChain) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -821,7 +940,7 @@ func (m *VscUnbondingOps) Reset() { *m = VscUnbondingOps{} } func (m *VscUnbondingOps) String() string { return proto.CompactTextString(m) } func (*VscUnbondingOps) ProtoMessage() {} func (*VscUnbondingOps) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{11} + return fileDescriptor_f22ec409a72b7b72, []int{12} } func (m *VscUnbondingOps) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -876,7 +995,7 @@ func (m *UnbondingOp) Reset() { *m = UnbondingOp{} } func (m *UnbondingOp) String() string { return proto.CompactTextString(m) } func (*UnbondingOp) ProtoMessage() {} func (*UnbondingOp) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{12} + return fileDescriptor_f22ec409a72b7b72, []int{13} } func (m *UnbondingOp) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -928,7 +1047,7 @@ func (m *InitTimeoutTimestamp) Reset() { *m = InitTimeoutTimestamp{} } func (m *InitTimeoutTimestamp) String() string { return proto.CompactTextString(m) } func (*InitTimeoutTimestamp) ProtoMessage() {} func (*InitTimeoutTimestamp) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{13} + return fileDescriptor_f22ec409a72b7b72, []int{14} } func (m *InitTimeoutTimestamp) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -980,7 +1099,7 @@ func (m *VscSendTimestamp) Reset() { *m = VscSendTimestamp{} } func (m *VscSendTimestamp) String() string { return proto.CompactTextString(m) } func (*VscSendTimestamp) ProtoMessage() {} func (*VscSendTimestamp) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{14} + return fileDescriptor_f22ec409a72b7b72, []int{15} } func (m *VscSendTimestamp) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1032,7 +1151,7 @@ func (m *ValidatorSetChangePackets) Reset() { *m = ValidatorSetChangePac func (m *ValidatorSetChangePackets) String() string { return proto.CompactTextString(m) } func (*ValidatorSetChangePackets) ProtoMessage() {} func (*ValidatorSetChangePackets) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{15} + return fileDescriptor_f22ec409a72b7b72, []int{16} } func (m *ValidatorSetChangePackets) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1078,7 +1197,7 @@ func (m *MaturedUnbondingOps) Reset() { *m = MaturedUnbondingOps{} } func (m *MaturedUnbondingOps) String() string { return proto.CompactTextString(m) } func (*MaturedUnbondingOps) ProtoMessage() {} func (*MaturedUnbondingOps) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{16} + return fileDescriptor_f22ec409a72b7b72, []int{17} } func (m *MaturedUnbondingOps) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1124,7 +1243,7 @@ func (m *ExportedVscSendTimestamp) Reset() { *m = ExportedVscSendTimesta func (m *ExportedVscSendTimestamp) String() string { return proto.CompactTextString(m) } func (*ExportedVscSendTimestamp) ProtoMessage() {} func (*ExportedVscSendTimestamp) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{17} + return fileDescriptor_f22ec409a72b7b72, []int{18} } func (m *ExportedVscSendTimestamp) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1177,7 +1296,7 @@ func (m *KeyAssignmentReplacement) Reset() { *m = KeyAssignmentReplaceme func (m *KeyAssignmentReplacement) String() string { return proto.CompactTextString(m) } func (*KeyAssignmentReplacement) ProtoMessage() {} func (*KeyAssignmentReplacement) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{18} + return fileDescriptor_f22ec409a72b7b72, []int{19} } func (m *KeyAssignmentReplacement) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1240,7 +1359,7 @@ func (m *ValidatorConsumerPubKey) Reset() { *m = ValidatorConsumerPubKey func (m *ValidatorConsumerPubKey) String() string { return proto.CompactTextString(m) } func (*ValidatorConsumerPubKey) ProtoMessage() {} func (*ValidatorConsumerPubKey) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{19} + return fileDescriptor_f22ec409a72b7b72, []int{20} } func (m *ValidatorConsumerPubKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1303,7 +1422,7 @@ func (m *ValidatorByConsumerAddr) Reset() { *m = ValidatorByConsumerAddr func (m *ValidatorByConsumerAddr) String() string { return proto.CompactTextString(m) } func (*ValidatorByConsumerAddr) ProtoMessage() {} func (*ValidatorByConsumerAddr) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{20} + return fileDescriptor_f22ec409a72b7b72, []int{21} } func (m *ValidatorByConsumerAddr) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1365,7 +1484,7 @@ func (m *ConsumerAddrsToPrune) Reset() { *m = ConsumerAddrsToPrune{} } func (m *ConsumerAddrsToPrune) String() string { return proto.CompactTextString(m) } func (*ConsumerAddrsToPrune) ProtoMessage() {} func (*ConsumerAddrsToPrune) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{21} + return fileDescriptor_f22ec409a72b7b72, []int{22} } func (m *ConsumerAddrsToPrune) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1430,7 +1549,7 @@ func (m *ConsumerValidator) Reset() { *m = ConsumerValidator{} } func (m *ConsumerValidator) String() string { return proto.CompactTextString(m) } func (*ConsumerValidator) ProtoMessage() {} func (*ConsumerValidator) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{22} + return fileDescriptor_f22ec409a72b7b72, []int{23} } func (m *ConsumerValidator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1491,7 +1610,7 @@ func (m *ConsumerRewardsAllocation) Reset() { *m = ConsumerRewardsAlloca func (m *ConsumerRewardsAllocation) String() string { return proto.CompactTextString(m) } func (*ConsumerRewardsAllocation) ProtoMessage() {} func (*ConsumerRewardsAllocation) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{23} + return fileDescriptor_f22ec409a72b7b72, []int{24} } func (m *ConsumerRewardsAllocation) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1530,6 +1649,7 @@ func (m *ConsumerRewardsAllocation) GetRewards() github_com_cosmos_cosmos_sdk_ty func init() { proto.RegisterType((*ConsumerAdditionProposal)(nil), "interchain_security.ccv.provider.v1.ConsumerAdditionProposal") proto.RegisterType((*ConsumerRemovalProposal)(nil), "interchain_security.ccv.provider.v1.ConsumerRemovalProposal") + proto.RegisterType((*ConsumerModificationProposal)(nil), "interchain_security.ccv.provider.v1.ConsumerModificationProposal") proto.RegisterType((*EquivocationProposal)(nil), "interchain_security.ccv.provider.v1.EquivocationProposal") proto.RegisterType((*ChangeRewardDenomsProposal)(nil), "interchain_security.ccv.provider.v1.ChangeRewardDenomsProposal") proto.RegisterType((*GlobalSlashEntry)(nil), "interchain_security.ccv.provider.v1.GlobalSlashEntry") @@ -1559,127 +1679,130 @@ func init() { } var fileDescriptor_f22ec409a72b7b72 = []byte{ - // 1919 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x4f, 0x6f, 0x1b, 0xc7, - 0x15, 0xd7, 0x8a, 0x94, 0x45, 0x0e, 0xf5, 0x77, 0xa4, 0xc4, 0x2b, 0x55, 0xa5, 0xe8, 0x4d, 0x93, - 0xaa, 0x71, 0xbd, 0x1b, 0x29, 0x2d, 0x60, 0x18, 0x0d, 0x02, 0x89, 0x72, 0x62, 0x59, 0x89, 0xcd, - 0xac, 0x54, 0x19, 0x6d, 0x0f, 0x8b, 0xe1, 0xec, 0x98, 0x1c, 0x68, 0xb9, 0xb3, 0x9e, 0x19, 0xae, - 0xc2, 0x4b, 0xcf, 0x3d, 0xb4, 0x40, 0x7a, 0x0b, 0x7a, 0x69, 0x5a, 0xa0, 0x40, 0xd1, 0x4b, 0xfb, - 0x31, 0x72, 0xcc, 0xb1, 0xa7, 0xa4, 0xb0, 0x0f, 0x3d, 0xf4, 0x4b, 0x14, 0x33, 0xfb, 0x97, 0x94, - 0xe4, 0xd2, 0x48, 0x73, 0x91, 0x76, 0xdf, 0xbc, 0xf7, 0x7b, 0x6f, 0xe6, 0xbd, 0x79, 0xbf, 0xc7, - 0x05, 0x7b, 0x34, 0x94, 0x84, 0xe3, 0x3e, 0xa2, 0xa1, 0x27, 0x08, 0x1e, 0x72, 0x2a, 0x47, 0x0e, - 0xc6, 0xb1, 0x13, 0x71, 0x16, 0x53, 0x9f, 0x70, 0x27, 0xde, 0xcd, 0x9f, 0xed, 0x88, 0x33, 0xc9, - 0xe0, 0x1b, 0x57, 0xd8, 0xd8, 0x18, 0xc7, 0x76, 0xae, 0x17, 0xef, 0x6e, 0xbe, 0x79, 0x1d, 0x70, - 0xbc, 0xeb, 0x5c, 0x50, 0x4e, 0x12, 0xac, 0xcd, 0xf5, 0x1e, 0xeb, 0x31, 0xfd, 0xe8, 0xa8, 0xa7, - 0x54, 0xba, 0xdd, 0x63, 0xac, 0x17, 0x10, 0x47, 0xbf, 0x75, 0x87, 0x4f, 0x1d, 0x49, 0x07, 0x44, - 0x48, 0x34, 0x88, 0x52, 0x85, 0xe6, 0xa4, 0x82, 0x3f, 0xe4, 0x48, 0x52, 0x16, 0x66, 0x00, 0xb4, - 0x8b, 0x1d, 0xcc, 0x38, 0x71, 0x70, 0x40, 0x49, 0x28, 0x95, 0xd7, 0xe4, 0x29, 0x55, 0x70, 0x94, - 0x42, 0x40, 0x7b, 0x7d, 0x99, 0x88, 0x85, 0x23, 0x49, 0xe8, 0x13, 0x3e, 0xa0, 0x89, 0x72, 0xf1, - 0x96, 0x1a, 0x6c, 0x95, 0xd6, 0x31, 0x1f, 0x45, 0x92, 0x39, 0xe7, 0x64, 0x24, 0xd2, 0xd5, 0xb7, - 0x30, 0x13, 0x03, 0x26, 0x1c, 0xa2, 0xf6, 0x1f, 0x62, 0xe2, 0xc4, 0xbb, 0x5d, 0x22, 0xd1, 0x6e, - 0x2e, 0xc8, 0xe2, 0x4e, 0xf5, 0xba, 0x48, 0x14, 0x3a, 0x98, 0xd1, 0x2c, 0xee, 0x55, 0x34, 0xa0, - 0x21, 0x73, 0xf4, 0xdf, 0x44, 0x64, 0xfd, 0xb6, 0x06, 0xcc, 0x36, 0x0b, 0xc5, 0x70, 0x40, 0xf8, - 0xbe, 0xef, 0x53, 0xb5, 0xcb, 0x0e, 0x67, 0x11, 0x13, 0x28, 0x80, 0xeb, 0x60, 0x4e, 0x52, 0x19, - 0x10, 0xd3, 0x68, 0x19, 0x3b, 0x75, 0x37, 0x79, 0x81, 0x2d, 0xd0, 0xf0, 0x89, 0xc0, 0x9c, 0x46, - 0x4a, 0xd9, 0x9c, 0xd5, 0x6b, 0x65, 0x11, 0xdc, 0x00, 0xb5, 0x24, 0x35, 0xd4, 0x37, 0x2b, 0x7a, - 0x79, 0x5e, 0xbf, 0x1f, 0xf9, 0xf0, 0x43, 0xb0, 0x44, 0x43, 0x2a, 0x29, 0x0a, 0xbc, 0x3e, 0x51, - 0x07, 0x64, 0x56, 0x5b, 0xc6, 0x4e, 0x63, 0x6f, 0xd3, 0xa6, 0x5d, 0x6c, 0xab, 0x33, 0xb5, 0xd3, - 0x93, 0x8c, 0x77, 0xed, 0x07, 0x5a, 0xe3, 0xa0, 0xfa, 0xe5, 0xd7, 0xdb, 0x33, 0xee, 0x62, 0x6a, - 0x97, 0x08, 0xe1, 0x2d, 0xb0, 0xd0, 0x23, 0x21, 0x11, 0x54, 0x78, 0x7d, 0x24, 0xfa, 0xe6, 0x5c, - 0xcb, 0xd8, 0x59, 0x70, 0x1b, 0xa9, 0xec, 0x01, 0x12, 0x7d, 0xb8, 0x0d, 0x1a, 0x5d, 0x1a, 0x22, - 0x3e, 0x4a, 0x34, 0x6e, 0x68, 0x0d, 0x90, 0x88, 0xb4, 0x42, 0x1b, 0x00, 0x11, 0xa1, 0x8b, 0xd0, - 0x53, 0x05, 0x60, 0xce, 0xa7, 0x81, 0x24, 0xc9, 0xb7, 0xb3, 0xe4, 0xdb, 0xa7, 0x59, 0x75, 0x1c, - 0xd4, 0x54, 0x20, 0x9f, 0x7d, 0xb3, 0x6d, 0xb8, 0x75, 0x6d, 0xa7, 0x56, 0xe0, 0x23, 0xb0, 0x32, - 0x0c, 0xbb, 0x2c, 0xf4, 0x69, 0xd8, 0xf3, 0x22, 0xc2, 0x29, 0xf3, 0xcd, 0x9a, 0x86, 0xda, 0xb8, - 0x04, 0x75, 0x98, 0xd6, 0x51, 0x82, 0xf4, 0xb9, 0x42, 0x5a, 0xce, 0x8d, 0x3b, 0xda, 0x16, 0x7e, - 0x02, 0x20, 0xc6, 0xb1, 0x0e, 0x89, 0x0d, 0x65, 0x86, 0x58, 0x9f, 0x1e, 0x71, 0x05, 0xe3, 0xf8, - 0x34, 0xb1, 0x4e, 0x21, 0x7f, 0x05, 0x6e, 0x4a, 0x8e, 0x42, 0xf1, 0x94, 0xf0, 0x49, 0x5c, 0x30, - 0x3d, 0xee, 0x6b, 0x19, 0xc6, 0x38, 0xf8, 0x03, 0xd0, 0xc2, 0x69, 0x01, 0x79, 0x9c, 0xf8, 0x54, - 0x48, 0x4e, 0xbb, 0x43, 0x65, 0xeb, 0x3d, 0xe5, 0x08, 0xeb, 0x1a, 0x69, 0xe8, 0x22, 0x68, 0x66, - 0x7a, 0xee, 0x98, 0xda, 0x07, 0xa9, 0x16, 0x7c, 0x0c, 0x7e, 0xd0, 0x0d, 0x18, 0x3e, 0x17, 0x2a, - 0x38, 0x6f, 0x0c, 0x49, 0xbb, 0x1e, 0x50, 0x21, 0x14, 0xda, 0x42, 0xcb, 0xd8, 0xa9, 0xb8, 0xb7, - 0x12, 0xdd, 0x0e, 0xe1, 0x87, 0x25, 0xcd, 0xd3, 0x92, 0x22, 0xbc, 0x03, 0x60, 0x9f, 0x0a, 0xc9, - 0x38, 0xc5, 0x28, 0xf0, 0x48, 0x28, 0x39, 0x25, 0xc2, 0x5c, 0xd4, 0xe6, 0xab, 0xc5, 0xca, 0xfd, - 0x64, 0x01, 0x3e, 0x04, 0xb7, 0xae, 0x75, 0xea, 0xe1, 0x3e, 0x0a, 0x43, 0x12, 0x98, 0x4b, 0x7a, - 0x2b, 0xdb, 0xfe, 0x35, 0x3e, 0xdb, 0x89, 0x1a, 0x5c, 0x03, 0x73, 0x92, 0x45, 0xde, 0x23, 0x73, - 0xb9, 0x65, 0xec, 0x2c, 0xba, 0x55, 0xc9, 0xa2, 0x47, 0xf0, 0x1d, 0xb0, 0x1e, 0xa3, 0x80, 0xfa, - 0x48, 0x32, 0x2e, 0xbc, 0x88, 0x5d, 0x10, 0xee, 0x61, 0x14, 0x99, 0x2b, 0x5a, 0x07, 0x16, 0x6b, - 0x1d, 0xb5, 0xd4, 0x46, 0x11, 0x7c, 0x1b, 0xac, 0xe6, 0x52, 0x4f, 0x10, 0xa9, 0xd5, 0x57, 0xb5, - 0xfa, 0x72, 0xbe, 0x70, 0x42, 0xa4, 0xd2, 0xdd, 0x02, 0x75, 0x14, 0x04, 0xec, 0x22, 0xa0, 0x42, - 0x9a, 0xb0, 0x55, 0xd9, 0xa9, 0xbb, 0x85, 0x00, 0x6e, 0x82, 0x9a, 0x4f, 0xc2, 0x91, 0x5e, 0x5c, - 0xd3, 0x8b, 0xf9, 0xfb, 0xbd, 0xda, 0x6f, 0xbe, 0xd8, 0x9e, 0xf9, 0xfc, 0x8b, 0xed, 0x19, 0xeb, - 0xef, 0x06, 0xb8, 0xd9, 0xce, 0xb3, 0x34, 0x60, 0x31, 0x0a, 0xbe, 0xcb, 0x6e, 0xb0, 0x0f, 0xea, - 0x42, 0x1d, 0x93, 0xbe, 0x7f, 0xd5, 0x57, 0xb8, 0x7f, 0x35, 0x65, 0xa6, 0x16, 0xac, 0x3f, 0x1a, - 0x60, 0xfd, 0xfe, 0xb3, 0x21, 0x8d, 0x19, 0x46, 0xff, 0x97, 0xe6, 0x75, 0x0c, 0x16, 0x49, 0x09, - 0x4f, 0x98, 0x95, 0x56, 0x65, 0xa7, 0xb1, 0xf7, 0xa6, 0x9d, 0x34, 0x57, 0x3b, 0xef, 0xb9, 0x69, - 0x83, 0xb5, 0xcb, 0xde, 0xdd, 0x71, 0xdb, 0x7b, 0xb3, 0xa6, 0x61, 0xfd, 0xd9, 0x00, 0x9b, 0xaa, - 0x2c, 0x7a, 0xc4, 0x25, 0x17, 0x88, 0xfb, 0x87, 0x24, 0x64, 0x03, 0xf1, 0xad, 0xe3, 0xb4, 0xc0, - 0xa2, 0xaf, 0x91, 0x3c, 0xc9, 0x3c, 0xe4, 0xfb, 0x3a, 0x4e, 0xad, 0xa3, 0x84, 0xa7, 0x6c, 0xdf, - 0xf7, 0xe1, 0x0e, 0x58, 0x29, 0x74, 0xb8, 0xca, 0xa7, 0x3a, 0x66, 0xa5, 0xb6, 0x94, 0xa9, 0xe9, - 0x2c, 0x13, 0xeb, 0x3f, 0x06, 0x58, 0xf9, 0x30, 0x60, 0x5d, 0x14, 0x9c, 0x04, 0x48, 0xf4, 0xd5, - 0x95, 0x18, 0xa9, 0xf4, 0x70, 0x92, 0xf6, 0x22, 0x1d, 0xde, 0xd4, 0xe9, 0x51, 0x66, 0xba, 0x3b, - 0xbe, 0x0f, 0x56, 0xf3, 0xee, 0x90, 0x57, 0x81, 0xde, 0xcd, 0xc1, 0xda, 0xf3, 0xaf, 0xb7, 0x97, - 0xb3, 0x62, 0x6b, 0xeb, 0x8a, 0x38, 0x74, 0x97, 0xf1, 0x98, 0xc0, 0x87, 0x4d, 0xd0, 0xa0, 0x5d, - 0xec, 0x09, 0xf2, 0xcc, 0x0b, 0x87, 0x03, 0x5d, 0x40, 0x55, 0xb7, 0x4e, 0xbb, 0xf8, 0x84, 0x3c, - 0x7b, 0x34, 0x1c, 0xc0, 0x77, 0xc1, 0xeb, 0xd9, 0x60, 0xe0, 0xc5, 0x28, 0xf0, 0x94, 0xbd, 0x3a, - 0x0e, 0xae, 0xeb, 0x69, 0xc1, 0x5d, 0xcb, 0x56, 0xcf, 0x50, 0xa0, 0x9c, 0xed, 0xfb, 0x3e, 0xb7, - 0x5e, 0xcc, 0x81, 0x1b, 0x1d, 0xc4, 0xd1, 0x40, 0xc0, 0x53, 0xb0, 0x2c, 0xc9, 0x20, 0x0a, 0x90, - 0x24, 0x5e, 0xc2, 0x3c, 0xe9, 0x4e, 0x6f, 0x6b, 0x46, 0x2a, 0x93, 0xb8, 0x5d, 0xa2, 0xed, 0x78, - 0xd7, 0x6e, 0x6b, 0xe9, 0x89, 0x44, 0x92, 0xb8, 0x4b, 0x19, 0x46, 0x22, 0x84, 0x77, 0x81, 0x29, - 0xf9, 0x50, 0xc8, 0x82, 0x13, 0x8a, 0x66, 0x98, 0xe4, 0xf2, 0xf5, 0x6c, 0x3d, 0x69, 0xa3, 0x79, - 0x13, 0xbc, 0xba, 0xfd, 0x57, 0xbe, 0x4d, 0xfb, 0x3f, 0x01, 0x6b, 0x8a, 0x3b, 0x27, 0x31, 0xab, - 0xd3, 0x63, 0xae, 0x2a, 0xfb, 0x71, 0xd0, 0x4f, 0x00, 0x8c, 0x05, 0x9e, 0xc4, 0x9c, 0x7b, 0x85, - 0x38, 0x63, 0x81, 0xc7, 0x21, 0x7d, 0xb0, 0x25, 0x54, 0xf1, 0x79, 0x03, 0x22, 0x35, 0x99, 0x44, - 0x01, 0x09, 0xa9, 0xe8, 0x67, 0xe0, 0x37, 0xa6, 0x07, 0xdf, 0xd0, 0x40, 0x1f, 0x2b, 0x1c, 0x37, - 0x83, 0x49, 0xbd, 0xb4, 0x41, 0xf3, 0x6a, 0x2f, 0x79, 0x82, 0xe6, 0x75, 0x82, 0xbe, 0x77, 0x05, - 0x44, 0x9e, 0x25, 0x01, 0xde, 0x2a, 0x91, 0x9e, 0xba, 0xd5, 0x9e, 0xbe, 0x50, 0x1e, 0x27, 0x3d, - 0xc5, 0x0c, 0x28, 0xe1, 0x3f, 0x42, 0x72, 0xe2, 0x4e, 0xbb, 0x87, 0x1a, 0xcd, 0xf2, 0xce, 0xd1, - 0x66, 0x34, 0x4c, 0xa7, 0x1b, 0xab, 0xe0, 0xc6, 0xbc, 0x47, 0xb8, 0x25, 0xac, 0x0f, 0x08, 0x51, - 0xb7, 0xb9, 0xc4, 0x8f, 0x24, 0x62, 0xb8, 0xaf, 0xf9, 0xbb, 0xe2, 0x2e, 0xe5, 0x5c, 0x78, 0x5f, - 0x49, 0x1f, 0x56, 0x6b, 0xb5, 0x95, 0xba, 0xf5, 0x23, 0x50, 0xd7, 0x97, 0x79, 0x1f, 0x9f, 0x0b, - 0xcd, 0x0e, 0xbe, 0xcf, 0x89, 0x10, 0x44, 0x98, 0x46, 0xca, 0x0e, 0x99, 0xc0, 0x92, 0x60, 0xe3, - 0xba, 0x29, 0x50, 0xc0, 0x27, 0x60, 0x3e, 0x22, 0x7a, 0x44, 0xd1, 0x86, 0x8d, 0xbd, 0xf7, 0xec, - 0x29, 0x66, 0x74, 0xfb, 0x3a, 0x40, 0x37, 0x43, 0xb3, 0x78, 0x31, 0x7b, 0x4e, 0x90, 0x8d, 0x80, - 0x67, 0x93, 0x4e, 0x7f, 0xf6, 0x4a, 0x4e, 0x27, 0xf0, 0x0a, 0x9f, 0xb7, 0x41, 0x63, 0x3f, 0xd9, - 0xf6, 0x47, 0x8a, 0x16, 0x2f, 0x1d, 0xcb, 0x42, 0xf9, 0x58, 0x1e, 0x82, 0xa5, 0x94, 0xd0, 0x4f, - 0x99, 0x6e, 0x48, 0xf0, 0xfb, 0x00, 0xa4, 0x93, 0x80, 0x6a, 0x64, 0x49, 0xcb, 0xae, 0xa7, 0x92, - 0x23, 0x7f, 0x8c, 0xeb, 0x66, 0xc7, 0xb8, 0xce, 0x72, 0xc1, 0xf2, 0x99, 0xc0, 0x3f, 0xcf, 0xa6, - 0xbd, 0xc7, 0x91, 0x80, 0xaf, 0x81, 0x1b, 0xea, 0x0e, 0xa5, 0x40, 0x55, 0x77, 0x2e, 0x16, 0xf8, - 0x48, 0x77, 0xed, 0x62, 0xa2, 0x64, 0x91, 0x47, 0x7d, 0x61, 0xce, 0xb6, 0x2a, 0x3b, 0x55, 0x77, - 0x69, 0x58, 0x98, 0x1f, 0xf9, 0xc2, 0xfa, 0x05, 0x68, 0x94, 0x00, 0xe1, 0x12, 0x98, 0xcd, 0xb1, - 0x66, 0xa9, 0x0f, 0xef, 0x81, 0x8d, 0x02, 0x68, 0xbc, 0x0d, 0x27, 0x88, 0x75, 0xf7, 0x66, 0xae, - 0x30, 0xd6, 0x89, 0x85, 0xf5, 0x18, 0xac, 0x1f, 0x15, 0x97, 0x3e, 0x6f, 0xf2, 0x63, 0x3b, 0x34, - 0xc6, 0xd9, 0x7c, 0x0b, 0xd4, 0xf3, 0x5f, 0x52, 0x7a, 0xf7, 0x55, 0xb7, 0x10, 0x58, 0x03, 0xb0, - 0x72, 0x26, 0xf0, 0x09, 0x09, 0xfd, 0x02, 0xec, 0x9a, 0x03, 0x38, 0x98, 0x04, 0x9a, 0x7a, 0x2c, - 0x2f, 0xdc, 0x31, 0xb0, 0x71, 0x56, 0x1e, 0x90, 0x34, 0x01, 0x77, 0x10, 0x3e, 0x27, 0x52, 0x40, - 0x17, 0x54, 0xf5, 0x20, 0x94, 0x54, 0xd6, 0xdd, 0x6b, 0x2b, 0x2b, 0xde, 0xb5, 0xaf, 0x03, 0x39, - 0x44, 0x12, 0xa5, 0x77, 0x57, 0x63, 0x59, 0x3f, 0x04, 0x6b, 0x1f, 0x23, 0x39, 0xe4, 0xc4, 0x1f, - 0xcb, 0xf1, 0x0a, 0xa8, 0xa8, 0xfc, 0x19, 0x3a, 0x7f, 0xea, 0x51, 0xcd, 0x03, 0xe6, 0xfd, 0x4f, - 0x23, 0xc6, 0x25, 0xf1, 0x2f, 0x9d, 0xc8, 0x4b, 0x8e, 0xf7, 0x1c, 0xac, 0xa9, 0xc3, 0x12, 0x24, - 0xf4, 0xbd, 0x7c, 0x9f, 0x49, 0x1e, 0x1b, 0x7b, 0x3f, 0x9d, 0xea, 0x76, 0x4c, 0xba, 0x4b, 0x37, - 0xb0, 0x1a, 0x4f, 0xc8, 0x85, 0xf5, 0x7b, 0x03, 0x98, 0xc7, 0x64, 0xb4, 0x2f, 0x04, 0xed, 0x85, - 0x03, 0x12, 0x4a, 0xd5, 0x03, 0x11, 0x26, 0xea, 0x11, 0xbe, 0x01, 0x16, 0x73, 0xce, 0xd5, 0x54, - 0x6b, 0x68, 0xaa, 0x5d, 0xc8, 0x84, 0xea, 0x82, 0xc1, 0x7b, 0x00, 0x44, 0x9c, 0xc4, 0x1e, 0xf6, - 0xce, 0xc9, 0x28, 0xcd, 0xe2, 0x56, 0x99, 0x42, 0x93, 0xdf, 0xb9, 0x76, 0x67, 0xd8, 0x0d, 0x28, - 0x3e, 0x26, 0x23, 0xb7, 0xa6, 0xf4, 0xdb, 0xc7, 0x64, 0xa4, 0x66, 0x22, 0x3d, 0x1d, 0x6b, 0xde, - 0xab, 0xb8, 0xc9, 0x8b, 0xf5, 0x07, 0x03, 0xdc, 0xcc, 0xd3, 0x91, 0x95, 0x6b, 0x67, 0xd8, 0x55, - 0x16, 0x2f, 0x39, 0xb7, 0x4b, 0xd1, 0xce, 0x5e, 0x11, 0xed, 0xfb, 0x60, 0x21, 0xbf, 0x20, 0x2a, - 0xde, 0xca, 0x14, 0xf1, 0x36, 0x32, 0x8b, 0x63, 0x32, 0xb2, 0x7e, 0x5d, 0x8a, 0xed, 0x60, 0x54, - 0xea, 0x7d, 0xfc, 0x7f, 0xc4, 0x96, 0xbb, 0x2d, 0xc7, 0x86, 0xcb, 0xf6, 0x97, 0x36, 0x50, 0xb9, - 0xbc, 0x01, 0xeb, 0x4f, 0x06, 0x58, 0x2f, 0x7b, 0x15, 0xa7, 0xac, 0xc3, 0x87, 0x21, 0x79, 0x99, - 0xf7, 0xe2, 0xfa, 0xcd, 0x96, 0xaf, 0xdf, 0x13, 0xb0, 0x34, 0x16, 0x94, 0x48, 0x4f, 0xe3, 0x9d, - 0xa9, 0x6a, 0xac, 0xd4, 0x5d, 0xdd, 0xc5, 0xf2, 0x3e, 0x84, 0xf5, 0x17, 0x03, 0xac, 0x66, 0x31, - 0xe6, 0x87, 0x05, 0x7f, 0x0c, 0x60, 0xbe, 0xbd, 0x62, 0x7a, 0x4b, 0x4a, 0x6a, 0x25, 0x5b, 0xc9, - 0x46, 0xb7, 0xa2, 0x34, 0x66, 0x4b, 0xa5, 0x01, 0x3f, 0x02, 0x6b, 0x79, 0xc8, 0x91, 0x4e, 0xd0, - 0xd4, 0x59, 0xcc, 0xe7, 0xd3, 0x5c, 0x64, 0xfd, 0xce, 0x28, 0xe8, 0x30, 0xe1, 0x63, 0xb1, 0x1f, - 0x04, 0xe9, 0x50, 0x0f, 0x23, 0x30, 0x9f, 0x50, 0xbe, 0x48, 0xfb, 0xc7, 0xd6, 0x95, 0xe4, 0x7e, - 0x48, 0xb0, 0xe6, 0xf7, 0xbb, 0xea, 0x8a, 0xfd, 0xed, 0x9b, 0xed, 0xdb, 0x3d, 0x2a, 0xfb, 0xc3, - 0xae, 0x8d, 0xd9, 0xc0, 0x49, 0xbf, 0xd3, 0x24, 0xff, 0xee, 0x08, 0xff, 0xdc, 0x91, 0xa3, 0x88, - 0x88, 0xcc, 0x46, 0xfc, 0xf5, 0xdf, 0xff, 0x78, 0xdb, 0x70, 0x33, 0x37, 0x07, 0x4f, 0xbe, 0x7c, - 0xde, 0x34, 0xbe, 0x7a, 0xde, 0x34, 0xfe, 0xf5, 0xbc, 0x69, 0x7c, 0xf6, 0xa2, 0x39, 0xf3, 0xd5, - 0x8b, 0xe6, 0xcc, 0x3f, 0x5f, 0x34, 0x67, 0x7e, 0xf9, 0xde, 0x65, 0xd0, 0x22, 0x47, 0x77, 0xf2, - 0x2f, 0x63, 0xf1, 0x4f, 0x9c, 0x4f, 0xc7, 0xbf, 0xbb, 0x69, 0x7f, 0xdd, 0x1b, 0xba, 0x9b, 0xbe, - 0xfb, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6d, 0xb7, 0x45, 0x0f, 0xa8, 0x13, 0x00, 0x00, + // 1954 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcb, 0x6f, 0x1b, 0xc7, + 0x19, 0xd7, 0x92, 0x94, 0x44, 0x0e, 0xf5, 0x1c, 0x29, 0xf1, 0x4a, 0x55, 0x29, 0x7a, 0xd3, 0xa4, + 0x6a, 0x5c, 0x2f, 0x23, 0xa5, 0x05, 0x0c, 0xa3, 0x41, 0x20, 0x51, 0x4e, 0x2c, 0x2b, 0xb6, 0x99, + 0x95, 0x2a, 0xa3, 0xed, 0x61, 0x31, 0x9c, 0x1d, 0x93, 0x03, 0x2d, 0x77, 0xd6, 0x33, 0xc3, 0x55, + 0x78, 0xe9, 0xb9, 0x87, 0x16, 0x48, 0x6f, 0x41, 0x0f, 0x6d, 0x5a, 0xa0, 0x40, 0xd1, 0x4b, 0xfb, + 0x67, 0xe4, 0x98, 0x63, 0x4f, 0x49, 0x61, 0x1f, 0x7a, 0xe8, 0x3f, 0x51, 0xcc, 0xec, 0x93, 0x94, + 0xe4, 0xd2, 0x48, 0x72, 0x91, 0x76, 0xbf, 0xc7, 0xef, 0xfb, 0x66, 0xbe, 0x27, 0x17, 0xec, 0xd1, + 0x40, 0x12, 0x8e, 0xfb, 0x88, 0x06, 0xae, 0x20, 0x78, 0xc8, 0xa9, 0x1c, 0xb5, 0x30, 0x8e, 0x5a, + 0x21, 0x67, 0x11, 0xf5, 0x08, 0x6f, 0x45, 0xbb, 0xd9, 0xb3, 0x1d, 0x72, 0x26, 0x19, 0x7c, 0xe3, + 0x0a, 0x1d, 0x1b, 0xe3, 0xc8, 0xce, 0xe4, 0xa2, 0xdd, 0xcd, 0x37, 0xaf, 0x03, 0x8e, 0x76, 0x5b, + 0x17, 0x94, 0x93, 0x18, 0x6b, 0x73, 0xbd, 0xc7, 0x7a, 0x4c, 0x3f, 0xb6, 0xd4, 0x53, 0x42, 0xdd, + 0xee, 0x31, 0xd6, 0xf3, 0x49, 0x4b, 0xbf, 0x75, 0x87, 0x4f, 0x5b, 0x92, 0x0e, 0x88, 0x90, 0x68, + 0x10, 0x26, 0x02, 0x8d, 0x49, 0x01, 0x6f, 0xc8, 0x91, 0xa4, 0x2c, 0x48, 0x01, 0x68, 0x17, 0xb7, + 0x30, 0xe3, 0xa4, 0x85, 0x7d, 0x4a, 0x02, 0xa9, 0xac, 0xc6, 0x4f, 0x89, 0x40, 0x4b, 0x09, 0xf8, + 0xb4, 0xd7, 0x97, 0x31, 0x59, 0xb4, 0x24, 0x09, 0x3c, 0xc2, 0x07, 0x34, 0x16, 0xce, 0xdf, 0x12, + 0x85, 0xad, 0x02, 0x1f, 0xf3, 0x51, 0x28, 0x59, 0xeb, 0x9c, 0x8c, 0x44, 0xc2, 0x7d, 0x0b, 0x33, + 0x31, 0x60, 0xa2, 0x45, 0xd4, 0xf9, 0x03, 0x4c, 0x5a, 0xd1, 0x6e, 0x97, 0x48, 0xb4, 0x9b, 0x11, + 0x52, 0xbf, 0x13, 0xb9, 0x2e, 0x12, 0xb9, 0x0c, 0x66, 0x34, 0xf5, 0x7b, 0x15, 0x0d, 0x68, 0xc0, + 0x5a, 0xfa, 0x6f, 0x4c, 0xb2, 0x7e, 0x5b, 0x05, 0x66, 0x9b, 0x05, 0x62, 0x38, 0x20, 0x7c, 0xdf, + 0xf3, 0xa8, 0x3a, 0x65, 0x87, 0xb3, 0x90, 0x09, 0xe4, 0xc3, 0x75, 0x30, 0x2b, 0xa9, 0xf4, 0x89, + 0x69, 0x34, 0x8d, 0x9d, 0x9a, 0x13, 0xbf, 0xc0, 0x26, 0xa8, 0x7b, 0x44, 0x60, 0x4e, 0x43, 0x25, + 0x6c, 0x96, 0x34, 0xaf, 0x48, 0x82, 0x1b, 0xa0, 0x1a, 0x87, 0x86, 0x7a, 0x66, 0x59, 0xb3, 0xe7, + 0xf5, 0xfb, 0x91, 0x07, 0x3f, 0x04, 0x4b, 0x34, 0xa0, 0x92, 0x22, 0xdf, 0xed, 0x13, 0x75, 0x41, + 0x66, 0xa5, 0x69, 0xec, 0xd4, 0xf7, 0x36, 0x6d, 0xda, 0xc5, 0xb6, 0xba, 0x53, 0x3b, 0xb9, 0xc9, + 0x68, 0xd7, 0xbe, 0xaf, 0x25, 0x0e, 0x2a, 0x5f, 0x7c, 0xb5, 0x3d, 0xe3, 0x2c, 0x26, 0x7a, 0x31, + 0x11, 0xde, 0x04, 0x0b, 0x3d, 0x12, 0x10, 0x41, 0x85, 0xdb, 0x47, 0xa2, 0x6f, 0xce, 0x36, 0x8d, + 0x9d, 0x05, 0xa7, 0x9e, 0xd0, 0xee, 0x23, 0xd1, 0x87, 0xdb, 0xa0, 0xde, 0xa5, 0x01, 0xe2, 0xa3, + 0x58, 0x62, 0x4e, 0x4b, 0x80, 0x98, 0xa4, 0x05, 0xda, 0x00, 0x88, 0x10, 0x5d, 0x04, 0xae, 0x4a, + 0x00, 0x73, 0x3e, 0x71, 0x24, 0x0e, 0xbe, 0x9d, 0x06, 0xdf, 0x3e, 0x4d, 0xb3, 0xe3, 0xa0, 0xaa, + 0x1c, 0xf9, 0xf4, 0xeb, 0x6d, 0xc3, 0xa9, 0x69, 0x3d, 0xc5, 0x81, 0x8f, 0xc0, 0xca, 0x30, 0xe8, + 0xb2, 0xc0, 0xa3, 0x41, 0xcf, 0x0d, 0x09, 0xa7, 0xcc, 0x33, 0xab, 0x1a, 0x6a, 0xe3, 0x12, 0xd4, + 0x61, 0x92, 0x47, 0x31, 0xd2, 0x67, 0x0a, 0x69, 0x39, 0x53, 0xee, 0x68, 0x5d, 0xf8, 0x31, 0x80, + 0x18, 0x47, 0xda, 0x25, 0x36, 0x94, 0x29, 0x62, 0x6d, 0x7a, 0xc4, 0x15, 0x8c, 0xa3, 0xd3, 0x58, + 0x3b, 0x81, 0xfc, 0x15, 0xb8, 0x21, 0x39, 0x0a, 0xc4, 0x53, 0xc2, 0x27, 0x71, 0xc1, 0xf4, 0xb8, + 0xaf, 0xa5, 0x18, 0xe3, 0xe0, 0xf7, 0x41, 0x13, 0x27, 0x09, 0xe4, 0x72, 0xe2, 0x51, 0x21, 0x39, + 0xed, 0x0e, 0x95, 0xae, 0xfb, 0x94, 0x23, 0xac, 0x73, 0xa4, 0xae, 0x93, 0xa0, 0x91, 0xca, 0x39, + 0x63, 0x62, 0x1f, 0x24, 0x52, 0xf0, 0x31, 0xf8, 0x41, 0xd7, 0x67, 0xf8, 0x5c, 0x28, 0xe7, 0xdc, + 0x31, 0x24, 0x6d, 0x7a, 0x40, 0x85, 0x50, 0x68, 0x0b, 0x4d, 0x63, 0xa7, 0xec, 0xdc, 0x8c, 0x65, + 0x3b, 0x84, 0x1f, 0x16, 0x24, 0x4f, 0x0b, 0x82, 0xf0, 0x36, 0x80, 0x7d, 0x2a, 0x24, 0xe3, 0x14, + 0x23, 0xdf, 0x25, 0x81, 0xe4, 0x94, 0x08, 0x73, 0x51, 0xab, 0xaf, 0xe6, 0x9c, 0x7b, 0x31, 0x03, + 0x3e, 0x00, 0x37, 0xaf, 0x35, 0xea, 0xe2, 0x3e, 0x0a, 0x02, 0xe2, 0x9b, 0x4b, 0xfa, 0x28, 0xdb, + 0xde, 0x35, 0x36, 0xdb, 0xb1, 0x18, 0x5c, 0x03, 0xb3, 0x92, 0x85, 0xee, 0x23, 0x73, 0xb9, 0x69, + 0xec, 0x2c, 0x3a, 0x15, 0xc9, 0xc2, 0x47, 0xf0, 0x1d, 0xb0, 0x1e, 0x21, 0x9f, 0x7a, 0x48, 0x32, + 0x2e, 0xdc, 0x90, 0x5d, 0x10, 0xee, 0x62, 0x14, 0x9a, 0x2b, 0x5a, 0x06, 0xe6, 0xbc, 0x8e, 0x62, + 0xb5, 0x51, 0x08, 0xdf, 0x06, 0xab, 0x19, 0xd5, 0x15, 0x44, 0x6a, 0xf1, 0x55, 0x2d, 0xbe, 0x9c, + 0x31, 0x4e, 0x88, 0x54, 0xb2, 0x5b, 0xa0, 0x86, 0x7c, 0x9f, 0x5d, 0xf8, 0x54, 0x48, 0x13, 0x36, + 0xcb, 0x3b, 0x35, 0x27, 0x27, 0xc0, 0x4d, 0x50, 0xf5, 0x48, 0x30, 0xd2, 0xcc, 0x35, 0xcd, 0xcc, + 0xde, 0xef, 0x56, 0x7f, 0xf3, 0xf9, 0xf6, 0xcc, 0x67, 0x9f, 0x6f, 0xcf, 0x58, 0xff, 0x30, 0xc0, + 0x8d, 0x76, 0x16, 0xa5, 0x01, 0x8b, 0x90, 0xff, 0x5d, 0x76, 0x83, 0x7d, 0x50, 0x13, 0xea, 0x9a, + 0x74, 0xfd, 0x55, 0x5e, 0xa1, 0xfe, 0xaa, 0x4a, 0x4d, 0x31, 0xac, 0x3f, 0x96, 0xc0, 0x56, 0xea, + 0xf1, 0x43, 0xe6, 0xd1, 0xa7, 0x14, 0xa3, 0xef, 0xba, 0x89, 0x65, 0xc1, 0xad, 0x4c, 0x11, 0xdc, + 0xd9, 0x57, 0x0b, 0xee, 0xdc, 0x14, 0xc1, 0x9d, 0x7f, 0x59, 0x70, 0xab, 0xe3, 0xc1, 0xb5, 0xfe, + 0x64, 0x80, 0xf5, 0x7b, 0xcf, 0x86, 0x34, 0x62, 0xdf, 0xd2, 0xc5, 0x1c, 0x83, 0x45, 0x52, 0xc0, + 0x13, 0x66, 0xb9, 0x59, 0xde, 0xa9, 0xef, 0xbd, 0x69, 0xc7, 0xd3, 0xc7, 0xce, 0x86, 0x52, 0x32, + 0x81, 0xec, 0xa2, 0x75, 0x67, 0x5c, 0xf7, 0x6e, 0xc9, 0x34, 0xac, 0xbf, 0x18, 0x60, 0x53, 0xd5, + 0x4d, 0x8f, 0x38, 0xe4, 0x02, 0x71, 0xef, 0x90, 0x04, 0x6c, 0x20, 0xbe, 0xb1, 0x9f, 0x16, 0x58, + 0xf4, 0x34, 0x92, 0x2b, 0x99, 0x8b, 0x3c, 0x4f, 0xfb, 0xa9, 0x65, 0x14, 0xf1, 0x94, 0xed, 0x7b, + 0x1e, 0xdc, 0x01, 0x2b, 0xb9, 0x0c, 0x57, 0x09, 0xaf, 0xf2, 0x50, 0x89, 0x2d, 0xa5, 0x62, 0xba, + 0x0c, 0x88, 0xf5, 0x5f, 0x03, 0xac, 0x7c, 0xe8, 0xb3, 0x2e, 0xf2, 0x4f, 0x7c, 0x24, 0xfa, 0xaa, + 0x67, 0x8c, 0x54, 0xfe, 0x72, 0x92, 0x34, 0x6b, 0xed, 0xde, 0xd4, 0xf9, 0xab, 0xd4, 0xf4, 0xf8, + 0x78, 0x1f, 0xac, 0x66, 0xed, 0x33, 0xcb, 0x37, 0x7d, 0x9a, 0x83, 0xb5, 0xe7, 0x5f, 0x6d, 0x2f, + 0xa7, 0xb9, 0xdd, 0xd6, 0xb9, 0x77, 0xe8, 0x2c, 0xe3, 0x31, 0x82, 0x07, 0x1b, 0xa0, 0x4e, 0xbb, + 0xd8, 0x15, 0xe4, 0x99, 0x1b, 0x0c, 0x07, 0x3a, 0x55, 0x2b, 0x4e, 0x8d, 0x76, 0xf1, 0x09, 0x79, + 0xf6, 0x68, 0x38, 0x80, 0xef, 0x82, 0xd7, 0xd3, 0xcd, 0xc9, 0x8d, 0x90, 0xef, 0x2a, 0x7d, 0x75, + 0x1d, 0x5c, 0x67, 0xef, 0x82, 0xb3, 0x96, 0x72, 0xcf, 0x90, 0xaf, 0x8c, 0xed, 0x7b, 0x1e, 0xb7, + 0x5e, 0xcc, 0x82, 0xb9, 0x0e, 0xe2, 0x68, 0x20, 0xe0, 0x29, 0x58, 0x96, 0x64, 0x10, 0xfa, 0x48, + 0x12, 0x37, 0x1e, 0xcd, 0xc9, 0x49, 0x6f, 0xe9, 0x91, 0x5d, 0xdc, 0x72, 0xec, 0xc2, 0x5e, 0x13, + 0xed, 0xda, 0x6d, 0x4d, 0x3d, 0x91, 0x48, 0x12, 0x67, 0x29, 0xc5, 0x88, 0x89, 0xf0, 0x0e, 0x30, + 0x25, 0x1f, 0x0a, 0x99, 0x0f, 0xcd, 0x7c, 0x5a, 0xc4, 0xb1, 0x7c, 0x3d, 0xe5, 0xc7, 0x73, 0x26, + 0x9b, 0x12, 0x57, 0xcf, 0xc7, 0xf2, 0x37, 0x99, 0x8f, 0x27, 0x60, 0x4d, 0x2d, 0x17, 0x93, 0x98, + 0x95, 0xe9, 0x31, 0x57, 0x95, 0xfe, 0x38, 0xe8, 0xc7, 0x00, 0x46, 0x02, 0x4f, 0x62, 0xce, 0xbe, + 0x82, 0x9f, 0x91, 0xc0, 0xe3, 0x90, 0x1e, 0xd8, 0x12, 0x2a, 0xf9, 0xdc, 0x01, 0x91, 0x7a, 0xda, + 0x86, 0x3e, 0x09, 0xa8, 0xe8, 0xa7, 0xe0, 0x73, 0xd3, 0x83, 0x6f, 0x68, 0xa0, 0x87, 0x0a, 0xc7, + 0x49, 0x61, 0x12, 0x2b, 0x6d, 0xd0, 0xb8, 0xda, 0x4a, 0x16, 0xa0, 0x79, 0x1d, 0xa0, 0xef, 0x5d, + 0x01, 0x91, 0x45, 0x49, 0x80, 0xb7, 0x0a, 0x5b, 0x81, 0xaa, 0x6a, 0x57, 0x17, 0x94, 0xcb, 0x49, + 0x4f, 0x8d, 0x4e, 0x14, 0x2f, 0x08, 0x84, 0x64, 0x9b, 0x4d, 0xd2, 0x3d, 0xd4, 0xee, 0x9a, 0x75, + 0x8e, 0x36, 0xa3, 0x41, 0xb2, 0xfe, 0x59, 0xf9, 0xf2, 0x90, 0xf5, 0x08, 0xa7, 0x80, 0xf5, 0x01, + 0x21, 0xaa, 0x9a, 0x0b, 0x0b, 0x04, 0x09, 0x19, 0xee, 0xeb, 0x05, 0xa7, 0xec, 0x2c, 0x65, 0xcb, + 0xc2, 0x3d, 0x45, 0x7d, 0x50, 0xa9, 0x56, 0x57, 0x6a, 0xd6, 0x8f, 0x40, 0x4d, 0x17, 0xf3, 0x3e, + 0x3e, 0x17, 0xba, 0xc3, 0x7a, 0x1e, 0x27, 0x42, 0x10, 0x61, 0x1a, 0x49, 0x87, 0x4d, 0x09, 0x96, + 0x04, 0x1b, 0xd7, 0xad, 0xc9, 0x02, 0x3e, 0x01, 0xf3, 0x21, 0xd1, 0x3b, 0x9c, 0x56, 0xac, 0xef, + 0xbd, 0x67, 0x4f, 0xf1, 0x23, 0xc6, 0xbe, 0x0e, 0xd0, 0x49, 0xd1, 0x2c, 0x9e, 0x2f, 0xe7, 0x13, + 0xd3, 0x58, 0xc0, 0xb3, 0x49, 0xa3, 0x3f, 0x7b, 0x25, 0xa3, 0x13, 0x78, 0xb9, 0xcd, 0x5b, 0xa0, + 0xbe, 0x1f, 0x1f, 0xfb, 0x23, 0x35, 0x5a, 0x2e, 0x5d, 0xcb, 0x42, 0xf1, 0x5a, 0x1e, 0x80, 0xa5, + 0x64, 0xe3, 0x39, 0x65, 0xba, 0x21, 0xc1, 0xef, 0x03, 0x90, 0xac, 0x4a, 0xaa, 0x91, 0xc5, 0x2d, + 0xbb, 0x96, 0x50, 0x8e, 0xbc, 0xb1, 0xa9, 0x5a, 0x1a, 0x9b, 0xaa, 0x96, 0x03, 0x96, 0xcf, 0x04, + 0xfe, 0x79, 0xba, 0x0e, 0x3f, 0x0e, 0x05, 0x7c, 0x0d, 0xcc, 0xa9, 0x1a, 0x4a, 0x80, 0x2a, 0xce, + 0x6c, 0x24, 0xf0, 0x91, 0xee, 0xda, 0xf9, 0xca, 0xcd, 0x42, 0x97, 0x7a, 0xc2, 0x2c, 0x35, 0xcb, + 0x3b, 0x15, 0x67, 0x69, 0x98, 0xab, 0x1f, 0x79, 0xc2, 0xfa, 0x05, 0xa8, 0x17, 0x00, 0xe1, 0x12, + 0x28, 0x65, 0x58, 0x25, 0xea, 0xc1, 0xbb, 0x60, 0x23, 0x07, 0x1a, 0x6f, 0xc3, 0x31, 0x62, 0xcd, + 0xb9, 0x91, 0x09, 0x8c, 0x75, 0x62, 0x61, 0x3d, 0x06, 0xeb, 0x47, 0x79, 0xd1, 0x67, 0x4d, 0x7e, + 0xec, 0x84, 0xc6, 0xf8, 0xde, 0xb0, 0x05, 0x6a, 0xd9, 0x4f, 0x4d, 0x7d, 0xfa, 0x8a, 0x93, 0x13, + 0xac, 0x01, 0x58, 0x39, 0x13, 0xf8, 0x84, 0x04, 0x5e, 0x0e, 0x76, 0xcd, 0x05, 0x1c, 0x4c, 0x02, + 0x4d, 0xfd, 0xbb, 0x25, 0x37, 0xc7, 0xc0, 0xc6, 0x59, 0x71, 0xc9, 0xd0, 0x03, 0xb8, 0x83, 0xf0, + 0x39, 0x91, 0x02, 0x3a, 0xa0, 0xa2, 0x97, 0x89, 0x38, 0xb3, 0xee, 0x5c, 0x9b, 0x59, 0xd1, 0xae, + 0x7d, 0x1d, 0xc8, 0x21, 0x92, 0x28, 0xa9, 0x5d, 0x8d, 0x65, 0xfd, 0x10, 0xac, 0x3d, 0x44, 0x72, + 0xc8, 0x89, 0x37, 0x16, 0xe3, 0x15, 0x50, 0x56, 0xf1, 0x33, 0x74, 0xfc, 0xd4, 0xa3, 0xda, 0x07, + 0xcc, 0x7b, 0x9f, 0x84, 0x8c, 0x4b, 0xe2, 0x5d, 0xba, 0x91, 0x97, 0x5c, 0xef, 0x39, 0x58, 0x53, + 0x97, 0x25, 0x48, 0xe0, 0xb9, 0xd9, 0x39, 0xe3, 0x38, 0xd6, 0xf7, 0x7e, 0x3a, 0x55, 0x75, 0x4c, + 0x9a, 0x4b, 0x0e, 0xb0, 0x1a, 0x4d, 0xd0, 0x85, 0xf5, 0x7b, 0x03, 0x98, 0xc7, 0x64, 0xb4, 0x2f, + 0x04, 0xed, 0x05, 0x03, 0x12, 0x48, 0xd5, 0x03, 0x11, 0x26, 0xea, 0x11, 0xbe, 0x01, 0x16, 0xb3, + 0x99, 0xab, 0x47, 0xad, 0xa1, 0x47, 0xed, 0x42, 0x4a, 0x54, 0x05, 0x06, 0xef, 0x02, 0x10, 0x72, + 0x12, 0xb9, 0xd8, 0x3d, 0x27, 0xa3, 0x24, 0x8a, 0x5b, 0xc5, 0x11, 0x1a, 0x7f, 0x08, 0xb0, 0x3b, + 0xc3, 0xae, 0x4f, 0xf1, 0x31, 0x19, 0x39, 0x55, 0x25, 0xdf, 0x3e, 0x26, 0x23, 0xb5, 0x13, 0xe9, + 0x0d, 0x53, 0xcf, 0xbd, 0xb2, 0x13, 0xbf, 0x58, 0x7f, 0x30, 0xc0, 0x8d, 0x2c, 0x1c, 0x69, 0xba, + 0x76, 0x86, 0x5d, 0xa5, 0xf1, 0x92, 0x7b, 0xbb, 0xe4, 0x6d, 0xe9, 0x0a, 0x6f, 0xdf, 0x07, 0x0b, + 0x59, 0x81, 0x28, 0x7f, 0xcb, 0x53, 0xf8, 0x5b, 0x4f, 0x35, 0x8e, 0xc9, 0xc8, 0xfa, 0x75, 0xc1, + 0xb7, 0x83, 0x51, 0xa1, 0xf7, 0xf1, 0xff, 0xe3, 0x5b, 0x66, 0xb6, 0xe8, 0x1b, 0x2e, 0xea, 0x5f, + 0x3a, 0x40, 0xf9, 0xf2, 0x01, 0xac, 0x3f, 0x1b, 0x60, 0xbd, 0x68, 0x55, 0x9c, 0xb2, 0x0e, 0x1f, + 0x06, 0xe4, 0x65, 0xd6, 0xf3, 0xf2, 0x2b, 0x15, 0xcb, 0xef, 0x09, 0x58, 0x1a, 0x73, 0x4a, 0x24, + 0xb7, 0xf1, 0xce, 0x54, 0x39, 0x56, 0xe8, 0xae, 0xce, 0x62, 0xf1, 0x1c, 0xc2, 0xfa, 0xab, 0x01, + 0x56, 0x53, 0x1f, 0xb3, 0xcb, 0x82, 0x3f, 0x06, 0x30, 0x3b, 0x5e, 0xbe, 0xbd, 0xc5, 0x29, 0xb5, + 0x92, 0x72, 0xd2, 0xd5, 0x2d, 0x4f, 0x8d, 0x52, 0x21, 0x35, 0xe0, 0x47, 0x60, 0x2d, 0x73, 0x39, + 0xd4, 0x01, 0x9a, 0x3a, 0x8a, 0xd9, 0x7e, 0x9a, 0x91, 0xac, 0xdf, 0x19, 0xf9, 0x38, 0x8c, 0xe7, + 0xb1, 0xd8, 0xf7, 0xfd, 0x64, 0xa9, 0x87, 0x21, 0x98, 0x8f, 0x47, 0xbe, 0x48, 0xfa, 0xc7, 0xd6, + 0x95, 0xc3, 0xfd, 0x90, 0x60, 0x3d, 0xdf, 0xef, 0xa8, 0x12, 0xfb, 0xfb, 0xd7, 0xdb, 0xb7, 0x7a, + 0x54, 0xf6, 0x87, 0x5d, 0x1b, 0xb3, 0x41, 0x2b, 0xf9, 0x90, 0x15, 0xff, 0xbb, 0x2d, 0xbc, 0xf3, + 0x96, 0x1c, 0x85, 0x44, 0xa4, 0x3a, 0xe2, 0x6f, 0xff, 0xf9, 0xe7, 0xdb, 0x86, 0x93, 0x9a, 0x39, + 0x78, 0xf2, 0xc5, 0xf3, 0x86, 0xf1, 0xe5, 0xf3, 0x86, 0xf1, 0xef, 0xe7, 0x0d, 0xe3, 0xd3, 0x17, + 0x8d, 0x99, 0x2f, 0x5f, 0x34, 0x66, 0xfe, 0xf5, 0xa2, 0x31, 0xf3, 0xcb, 0xf7, 0x2e, 0x83, 0xe6, + 0x31, 0xba, 0x9d, 0x7d, 0x3a, 0x8c, 0x7e, 0xd2, 0xfa, 0x64, 0xfc, 0xc3, 0xa4, 0xb6, 0xd7, 0x9d, + 0xd3, 0xdd, 0xf4, 0xdd, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x24, 0x1f, 0x07, 0x8b, 0xc9, 0x14, + 0x00, 0x00, } func (m *ConsumerAdditionProposal) Marshal() (dAtA []byte, err error) { @@ -1899,6 +2022,83 @@ func (m *ConsumerRemovalProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } +func (m *ConsumerModificationProposal) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ConsumerModificationProposal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConsumerModificationProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Denylist) > 0 { + for iNdEx := len(m.Denylist) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Denylist[iNdEx]) + copy(dAtA[i:], m.Denylist[iNdEx]) + i = encodeVarintProvider(dAtA, i, uint64(len(m.Denylist[iNdEx]))) + i-- + dAtA[i] = 0x42 + } + } + if len(m.Allowlist) > 0 { + for iNdEx := len(m.Allowlist) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Allowlist[iNdEx]) + copy(dAtA[i:], m.Allowlist[iNdEx]) + i = encodeVarintProvider(dAtA, i, uint64(len(m.Allowlist[iNdEx]))) + i-- + dAtA[i] = 0x3a + } + } + if m.ValidatorSetCap != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.ValidatorSetCap)) + i-- + dAtA[i] = 0x30 + } + if m.ValidatorsPowerCap != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.ValidatorsPowerCap)) + i-- + dAtA[i] = 0x28 + } + if m.Top_N != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.Top_N)) + i-- + dAtA[i] = 0x20 + } + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintProvider(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0x1a + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintProvider(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintProvider(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *EquivocationProposal) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -2981,6 +3181,48 @@ func (m *ConsumerRemovalProposal) Size() (n int) { return n } +func (m *ConsumerModificationProposal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Title) + if l > 0 { + n += 1 + l + sovProvider(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovProvider(uint64(l)) + } + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovProvider(uint64(l)) + } + if m.Top_N != 0 { + n += 1 + sovProvider(uint64(m.Top_N)) + } + if m.ValidatorsPowerCap != 0 { + n += 1 + sovProvider(uint64(m.ValidatorsPowerCap)) + } + if m.ValidatorSetCap != 0 { + n += 1 + sovProvider(uint64(m.ValidatorSetCap)) + } + if len(m.Allowlist) > 0 { + for _, s := range m.Allowlist { + l = len(s) + n += 1 + l + sovProvider(uint64(l)) + } + } + if len(m.Denylist) > 0 { + for _, s := range m.Denylist { + l = len(s) + n += 1 + l + sovProvider(uint64(l)) + } + } + return n +} + func (m *EquivocationProposal) Size() (n int) { if m == nil { return 0 @@ -4187,6 +4429,273 @@ func (m *ConsumerRemovalProposal) Unmarshal(dAtA []byte) error { } return nil } +func (m *ConsumerModificationProposal) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ConsumerModificationProposal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConsumerModificationProposal: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Top_N", wireType) + } + m.Top_N = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Top_N |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorsPowerCap", wireType) + } + m.ValidatorsPowerCap = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ValidatorsPowerCap |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorSetCap", wireType) + } + m.ValidatorSetCap = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ValidatorSetCap |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Allowlist", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Allowlist = append(m.Allowlist, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denylist", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denylist = append(m.Denylist, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipProvider(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProvider + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *EquivocationProposal) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 From bbe1c548b2b2b9a419d9ac1ed8aa1b0936dabdc9 Mon Sep 17 00:00:00 2001 From: insumity Date: Wed, 5 Jun 2024 14:52:42 +0200 Subject: [PATCH 053/102] fix!: apply audit suggestions (#1925) * init commit * added CHANGELOG entries * added nit simplification change * addressed comment by Hypha * took into account err returned by ComputeMinPowerToOptIn * fixed test failing * build(deps): bump github.com/cosmos/ibc-go/v7 from 7.5.0 to 7.5.1 (#1924) * build(deps): bump github.com/cosmos/ibc-go/v7 from 7.5.0 to 7.5.1 Bumps [github.com/cosmos/ibc-go/v7](https://github.com/cosmos/ibc-go) from 7.5.0 to 7.5.1. - [Release notes](https://github.com/cosmos/ibc-go/releases) - [Changelog](https://github.com/cosmos/ibc-go/blob/v7.5.1/CHANGELOG.md) - [Commits](https://github.com/cosmos/ibc-go/compare/v7.5.0...v7.5.1) --- updated-dependencies: - dependency-name: github.com/cosmos/ibc-go/v7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * update changelog entry --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: mpoke * build(deps): bump bufbuild/buf-setup-action from 1.32.0 to 1.32.1 (#1923) Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.32.0 to 1.32.1. - [Release notes](https://github.com/bufbuild/buf-setup-action/releases) - [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.32.0...v1.32.1) --- updated-dependencies: - dependency-name: bufbuild/buf-setup-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump bufbuild/buf-setup-action from 1.32.1 to 1.32.2 (#1934) Bumps [bufbuild/buf-setup-action](https://github.com/bufbuild/buf-setup-action) from 1.32.1 to 1.32.2. - [Release notes](https://github.com/bufbuild/buf-setup-action/releases) - [Commits](https://github.com/bufbuild/buf-setup-action/compare/v1.32.1...v1.32.2) --- updated-dependencies: - dependency-name: bufbuild/buf-setup-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump github.com/spf13/viper from 1.18.2 to 1.19.0 (#1936) Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.18.2 to 1.19.0. - [Release notes](https://github.com/spf13/viper/releases) - [Commits](https://github.com/spf13/viper/compare/v1.18.2...v1.19.0) --- updated-dependencies: - dependency-name: github.com/spf13/viper dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump docker/login-action from 3.1.0 to 3.2.0 (#1935) Bumps [docker/login-action](https://github.com/docker/login-action) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/e92390c5fb421da1463c202d546fed0ec5c39f20...0d4c9c5ea7693da7b068278f7b52bda2a190a446) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * took into account comments --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: mpoke --- .../provider/1925-apply-audit-suggestions.md | 3 + .../provider/1925-apply-audit-suggestions.md | 3 + docs/docs/features/power-shaping.md | 4 +- tests/e2e/steps_partial_set_security.go | 22 +++---- x/ccv/consumer/keeper/soft_opt_out.go | 2 +- x/ccv/provider/keeper/distribution.go | 12 ++-- x/ccv/provider/keeper/keeper.go | 26 +++----- x/ccv/provider/keeper/msg_server.go | 7 +-- x/ccv/provider/keeper/partial_set_security.go | 62 ++++++++++--------- .../keeper/partial_set_security_test.go | 49 +++++++-------- x/ccv/provider/keeper/proposal.go | 4 +- x/ccv/provider/keeper/relay.go | 7 ++- x/ccv/provider/keeper/validator_set_update.go | 4 +- 13 files changed, 104 insertions(+), 101 deletions(-) create mode 100644 .changelog/unreleased/bug-fixes/provider/1925-apply-audit-suggestions.md create mode 100644 .changelog/unreleased/state-breaking/provider/1925-apply-audit-suggestions.md diff --git a/.changelog/unreleased/bug-fixes/provider/1925-apply-audit-suggestions.md b/.changelog/unreleased/bug-fixes/provider/1925-apply-audit-suggestions.md new file mode 100644 index 0000000000..3d12033a4e --- /dev/null +++ b/.changelog/unreleased/bug-fixes/provider/1925-apply-audit-suggestions.md @@ -0,0 +1,3 @@ +- Apply audit suggestions that include a bug fix in the way we compute the + maximum capped power. ([\#1925](https://github.com/cosmos/interchain- + security/pull/1925)) diff --git a/.changelog/unreleased/state-breaking/provider/1925-apply-audit-suggestions.md b/.changelog/unreleased/state-breaking/provider/1925-apply-audit-suggestions.md new file mode 100644 index 0000000000..3d12033a4e --- /dev/null +++ b/.changelog/unreleased/state-breaking/provider/1925-apply-audit-suggestions.md @@ -0,0 +1,3 @@ +- Apply audit suggestions that include a bug fix in the way we compute the + maximum capped power. ([\#1925](https://github.com/cosmos/interchain- + security/pull/1925)) diff --git a/docs/docs/features/power-shaping.md b/docs/docs/features/power-shaping.md index 9884c6184b..726ff031c6 100644 --- a/docs/docs/features/power-shaping.md +++ b/docs/docs/features/power-shaping.md @@ -6,7 +6,9 @@ several "power shaping" mechanisms for consumer chains. These are: 1) **Capping the size of the validator set**: The consumer chain can specify a maximum number of validators it wants to have in its validator set. This can be used to limit the number of validators in the set, which can -be useful for chains that want to have a smaller validator set for faster blocks or lower overhead. +be useful for chains that want to have a smaller validator set for faster blocks or lower overhead. If more validators +than the maximum size have opted in on a consumer chain, only the validators with the highest power, up to the specified +maximum, will validate the consumer chain. :::info This is only applicable to Opt In chains (chains with Top N = 0). ::: diff --git a/tests/e2e/steps_partial_set_security.go b/tests/e2e/steps_partial_set_security.go index 164ff0d067..5e86ede3a8 100644 --- a/tests/e2e/steps_partial_set_security.go +++ b/tests/e2e/steps_partial_set_security.go @@ -1391,11 +1391,11 @@ func stepsValidatorsPowerCappedChain() []Step { ChainID("consu"): ChainState{ ValPowers: &map[ValidatorID]uint{ // the powers of the validators on the consumer chain are different from the provider chain - // because the consumer chain is power capped. Note that the total power is 600 (= 194 + 203 + 203) - // and 203 / 600.0 = 0.338 < 34% that is the power cap. - ValidatorID("alice"): 194, - ValidatorID("bob"): 203, - ValidatorID("carol"): 203, + // because the consumer chain is power capped. Note that the total power is 600 (= 192 + 204 + 204) + // and 204 / 600.0 = 0.34 <= 34% that is the power cap. + ValidatorID("alice"): 192, + ValidatorID("bob"): 204, + ValidatorID("carol"): 204, }, }, }, @@ -1428,9 +1428,9 @@ func stepsValidatorsPowerCappedChain() []Step { State: State{ ChainID("consu"): ChainState{ ValPowers: &map[ValidatorID]uint{ - ValidatorID("alice"): 194, - ValidatorID("bob"): 203, - ValidatorID("carol"): 203, + ValidatorID("alice"): 192, + ValidatorID("bob"): 204, + ValidatorID("carol"): 204, }, }, ChainID("provi"): ChainState{ @@ -1454,9 +1454,9 @@ func stepsValidatorsPowerCappedChain() []Step { ValPowers: &map[ValidatorID]uint{ // "carol" has opted out, and we only have 2 validators left validating. Power capping only operates // in a best-effort basis and with 2 validators we cannot guarantee that no validator has more - // than 34% of the voting power. In this case, both validators get the same voting power (50% = 101 / 202). - ValidatorID("alice"): 101, - ValidatorID("bob"): 101, + // than 34% of the voting power. In this case, both validators get the same voting power (50% = 102 / 204). + ValidatorID("alice"): 102, + ValidatorID("bob"): 102, ValidatorID("carol"): 0, }, }, diff --git a/x/ccv/consumer/keeper/soft_opt_out.go b/x/ccv/consumer/keeper/soft_opt_out.go index e5990ff65d..9221cca851 100644 --- a/x/ccv/consumer/keeper/soft_opt_out.go +++ b/x/ccv/consumer/keeper/soft_opt_out.go @@ -55,7 +55,7 @@ func (k Keeper) UpdateSmallestNonOptOutPower(ctx sdk.Context) { // get total power in set totalPower := sdk.ZeroDec() for _, val := range valset { - totalPower = totalPower.Add(sdk.NewDecFromInt(sdk.NewInt(val.Power))) + totalPower = totalPower.Add(sdk.NewDec(val.Power)) } // get power of the smallest validator that cannot soft opt out diff --git a/x/ccv/provider/keeper/distribution.go b/x/ccv/provider/keeper/distribution.go index 67e6193e5d..f59f111301 100644 --- a/x/ccv/provider/keeper/distribution.go +++ b/x/ccv/provider/keeper/distribution.go @@ -140,8 +140,8 @@ func (k Keeper) AllocateTokensToConsumerValidators( } // get the total voting power of the consumer valset - totalPower := k.ComputeConsumerTotalVotingPower(ctx, chainID) - if totalPower == 0 { + totalPower := math.LegacyNewDec(k.ComputeConsumerTotalVotingPower(ctx, chainID)) + if totalPower.IsZero() { return allocated } @@ -150,7 +150,7 @@ func (k Keeper) AllocateTokensToConsumerValidators( consAddr := sdk.ConsAddress(consumerVal.ProviderConsAddr) // get the validator tokens fraction using its voting power - powerFraction := math.LegacyNewDec(consumerVal.Power).QuoTruncate(math.LegacyNewDec(totalPower)) + powerFraction := math.LegacyNewDec(consumerVal.Power).QuoTruncate(totalPower) tokensFraction := tokens.MulDecTruncate(powerFraction) // get the validator type struct for the consensus address @@ -190,14 +190,14 @@ func (k Keeper) TransferConsumerRewardsToDistributionModule( } // Truncate coin rewards - rewardsToSend, _ := allocation.Rewards.TruncateDecimal() + rewardsToSend, remRewards := allocation.Rewards.TruncateDecimal() // NOTE the consumer rewards allocation isn't a module account, however its coins // are held in the consumer reward pool module account. Thus the consumer // rewards allocation must be reduced separately from the SendCoinsFromModuleToAccount call. // Update consumer rewards allocation with the remaining decimal coins - allocation.Rewards = allocation.Rewards.Sub(sdk.NewDecCoinsFromCoins(rewardsToSend...)) + allocation.Rewards = remRewards // Send coins to distribution module account err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.ConsumerRewardsPool, distrtypes.ModuleName, rewardsToSend) @@ -238,7 +238,7 @@ func (k Keeper) GetConsumerRewardsPool(ctx sdk.Context) sdk.Coins { // ComputeConsumerTotalVotingPower returns the validator set total voting power // for the given consumer chain func (k Keeper) ComputeConsumerTotalVotingPower(ctx sdk.Context, chainID string) (totalPower int64) { - // sum the opted-in validators set voting powers + // sum the consumer validators set voting powers for _, v := range k.GetConsumerValSet(ctx, chainID) { totalPower += v.Power } diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 98930a3085..438cb5aa67 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -257,7 +257,7 @@ func (k Keeper) GetAllConsumerChains(ctx sdk.Context) (chains []types.Chain) { var minPowerInTopN int64 if found && topN > 0 { - res, err := k.ComputeMinPowerToOptIn(ctx, chainID, k.stakingKeeper.GetLastValidators(ctx), topN) + res, err := k.ComputeMinPowerToOptIn(ctx, k.stakingKeeper.GetLastValidators(ctx), topN) if err != nil { k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err) minPowerInTopN = -1 @@ -759,13 +759,9 @@ func (k Keeper) GetValidatorSetUpdateId(ctx sdk.Context) (validatorSetUpdateId u bz := store.Get(types.ValidatorSetUpdateIdKey()) if bz == nil { - validatorSetUpdateId = 0 - } else { - // Unmarshal - validatorSetUpdateId = binary.BigEndian.Uint64(bz) + return 0 } - - return validatorSetUpdateId + return binary.BigEndian.Uint64(bz) } // SetValsetUpdateBlockHeight sets the block height for a given valset update id @@ -1302,9 +1298,11 @@ func (k Keeper) HasToValidate( bondedValidators := k.stakingKeeper.GetLastValidators(ctx) if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { // in a Top-N chain, we automatically opt in all validators that belong to the top N - minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, bondedValidators, topN) + minPower, err := k.ComputeMinPowerToOptIn(ctx, bondedValidators, topN) if err == nil { k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) + } else { + k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err) } } @@ -1528,11 +1526,7 @@ func (k Keeper) IsAllowlistEmpty(ctx sdk.Context, chainID string) bool { iterator := sdk.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.AllowlistPrefix, chainID)) defer iterator.Close() - if iterator.Valid() { - return false - } - - return true + return !iterator.Valid() } // SetDenylist denylists validator with `providerAddr` address on chain `chainID` @@ -1595,9 +1589,5 @@ func (k Keeper) IsDenylistEmpty(ctx sdk.Context, chainID string) bool { iterator := sdk.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.DenylistPrefix, chainID)) defer iterator.Close() - if iterator.Valid() { - return false - } - - return true + return !iterator.Valid() } diff --git a/x/ccv/provider/keeper/msg_server.go b/x/ccv/provider/keeper/msg_server.go index 1f7f51b589..c4c7a04085 100644 --- a/x/ccv/provider/keeper/msg_server.go +++ b/x/ccv/provider/keeper/msg_server.go @@ -157,12 +157,7 @@ func (k msgServer) OptIn(goCtx context.Context, msg *types.MsgOptIn) (*types.Msg } providerConsAddr := types.NewProviderConsAddress(consAddrTmp) - if msg.ConsumerKey != "" { - err = k.Keeper.HandleOptIn(ctx, msg.ChainId, providerConsAddr, &msg.ConsumerKey) - } else { - err = k.Keeper.HandleOptIn(ctx, msg.ChainId, providerConsAddr, nil) - } - + err = k.Keeper.HandleOptIn(ctx, msg.ChainId, providerConsAddr, msg.ConsumerKey) if err != nil { return nil, err } diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index 0d5177089a..1810bcc0f8 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -14,7 +14,7 @@ import ( // HandleOptIn prepares validator `providerAddr` to opt in to `chainID` with an optional `consumerKey` consumer public key. // Note that the validator only opts in at the end of an epoch. -func (k Keeper) HandleOptIn(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress, consumerKey *string) error { +func (k Keeper) HandleOptIn(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress, consumerKey string) error { if !k.IsConsumerProposedOrRegistered(ctx, chainID) { return errorsmod.Wrapf( types.ErrUnknownConsumerChainId, @@ -23,8 +23,8 @@ func (k Keeper) HandleOptIn(ctx sdk.Context, chainID string, providerAddr types. k.SetOptedIn(ctx, chainID, providerAddr) - if consumerKey != nil { - consumerTMPublicKey, err := k.ParseConsumerKey(*consumerKey) + if consumerKey != "" { + consumerTMPublicKey, err := k.ParseConsumerKey(consumerKey) if err != nil { return err } @@ -63,13 +63,22 @@ func (k Keeper) HandleOptOut(ctx sdk.Context, chainID string, providerAddr types "validator with consensus address %s could not be found", providerAddr.ToSdkConsAddr()) } power := k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()) - minPowerToOptIn, err := k.ComputeMinPowerToOptIn(ctx, chainID, k.stakingKeeper.GetLastValidators(ctx), topN) + minPowerToOptIn, err := k.ComputeMinPowerToOptIn(ctx, k.stakingKeeper.GetLastValidators(ctx), topN) - if err != nil || power >= minPowerToOptIn { + if err != nil { + k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err) return errorsmod.Wrapf( types.ErrCannotOptOutFromTopN, - "validator with power (%d) cannot opt out from Top N chain because all validators"+ - "with at least %d power have to validate", power, minPowerToOptIn) + "validator with power (%d) cannot opt out from Top N chain (%s) because the min power"+ + " could not be computed: %s", power, chainID, err.Error()) + + } + + if power >= minPowerToOptIn { + return errorsmod.Wrapf( + types.ErrCannotOptOutFromTopN, + "validator with power (%d) cannot opt out from Top N chain (%s) because all validators"+ + " with at least %d power have to validate", power, chainID, minPowerToOptIn) } } @@ -97,11 +106,14 @@ func (k Keeper) OptInTopNValidators(ctx sdk.Context, chainID string, bondedValid } // ComputeMinPowerToOptIn returns the minimum power needed for a validator (from the bonded validators) -// to belong to the `topN` validators. `chainID` is only used for logging purposes. -func (k Keeper) ComputeMinPowerToOptIn(ctx sdk.Context, chainID string, bondedValidators []stakingtypes.Validator, topN uint32) (int64, error) { +// to belong to the `topN` validators for a Top N chain. +func (k Keeper) ComputeMinPowerToOptIn(ctx sdk.Context, bondedValidators []stakingtypes.Validator, topN uint32) (int64, error) { if topN == 0 || topN > 100 { - return 0, fmt.Errorf("trying to compute minimum power with an incorrect topN value (%d)."+ - "topN has to be between (0, 100]", topN) + // Note that Top N chains have a lower limit on `topN`, namely that topN cannot be less than 50. + // However, we can envision that this method could be used for other (future) reasons where this might not + // be the case. For this, this method operates for `topN`s in (0, 100]. + return 0, fmt.Errorf("trying to compute minimum power with an incorrect"+ + " topN value (%d). topN has to be in (0, 100]", topN) } totalPower := sdk.ZeroDec() @@ -110,7 +122,7 @@ func (k Keeper) ComputeMinPowerToOptIn(ctx sdk.Context, chainID string, bondedVa for _, val := range bondedValidators { power := k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()) powers = append(powers, power) - totalPower = totalPower.Add(sdk.NewDecFromInt(sdk.NewInt(power))) + totalPower = totalPower.Add(sdk.NewDec(power)) } // sort by powers descending @@ -118,10 +130,10 @@ func (k Keeper) ComputeMinPowerToOptIn(ctx sdk.Context, chainID string, bondedVa return powers[i] > powers[j] }) - topNThreshold := sdk.NewDecFromInt(sdk.NewInt(int64(topN))).QuoInt64(int64(100)) + topNThreshold := sdk.NewDec(int64(topN)).QuoInt64(int64(100)) powerSum := sdk.ZeroDec() for _, power := range powers { - powerSum = powerSum.Add(sdk.NewDecFromInt(sdk.NewInt(power))) + powerSum = powerSum.Add(sdk.NewDec(power)) if powerSum.Quo(totalPower).GTE(topNThreshold) { return power, nil } @@ -140,18 +152,12 @@ func (k Keeper) CapValidatorSet(ctx sdk.Context, chainID string, validators []ty return validators } - if validatorSetCap, found := k.GetValidatorSetCap(ctx, chainID); found && validatorSetCap != 0 { + if validatorSetCap, found := k.GetValidatorSetCap(ctx, chainID); found && validatorSetCap != 0 && int(validatorSetCap) < len(validators) { sort.Slice(validators, func(i, j int) bool { return validators[i].Power > validators[j].Power }) - minNumberOfValidators := 0 - if len(validators) < int(validatorSetCap) { - minNumberOfValidators = len(validators) - } else { - minNumberOfValidators = int(validatorSetCap) - } - return validators[:minNumberOfValidators] + return validators[:int(validatorSetCap)] } else { return validators } @@ -209,9 +215,8 @@ func NoMoreThanPercentOfTheSum(validators []types.ConsumerValidator, percent uin // If `s > n * maxPower` there's no solution and the algorithm would set everything to `maxPower`. // ---------------- - // Computes `(sum(validators) * percent) / 100`. Because `sdk.Dec` does not provide a `Floor` function, but only - // a `Ceil` one, we use `Ceil` and subtract one. - maxPower := sdk.NewDec(sum(validators)).Mul(sdk.NewDec(int64(percent))).QuoInt64(100).Ceil().RoundInt64() - 1 + // Computes `floor((sum(validators) * percent) / 100)` + maxPower := sdk.NewDec(sum(validators)).Mul(sdk.NewDec(int64(percent))).QuoInt64(100).TruncateInt64() if maxPower == 0 { // edge case: set `maxPower` to 1 to avoid setting the power of a validator to 0 @@ -271,8 +276,9 @@ func NoMoreThanPercentOfTheSum(validators []types.ConsumerValidator, percent uin return updatedValidators } -// FilterOptedInAndAllowAndDenylistedPredicate filters the opted-in validators that are allowlisted and not denylisted -func (k Keeper) FilterOptedInAndAllowAndDenylistedPredicate(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress) bool { +// CanValidateChain returns true if the validator `providerAddr` is opted-in to chain `chainID` and the allowlist and +// denylist do not prevent the validator from validating the chain. +func (k Keeper) CanValidateChain(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress) bool { // only consider opted-in validators return k.IsOptedIn(ctx, chainID, providerAddr) && // if an allowlist is declared, only consider allowlisted validators @@ -287,7 +293,7 @@ func (k Keeper) FilterOptedInAndAllowAndDenylistedPredicate(ctx sdk.Context, cha func (k Keeper) ComputeNextValidators(ctx sdk.Context, chainID string, bondedValidators []stakingtypes.Validator) []types.ConsumerValidator { nextValidators := k.FilterValidators(ctx, chainID, bondedValidators, func(providerAddr types.ProviderConsAddress) bool { - return k.FilterOptedInAndAllowAndDenylistedPredicate(ctx, chainID, providerAddr) + return k.CanValidateChain(ctx, chainID, providerAddr) }) nextValidators = k.CapValidatorSet(ctx, chainID, nextValidators) diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index afbd420e7f..cb1fbab9d4 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -30,11 +30,11 @@ func TestHandleOptIn(t *testing.T) { providerAddr := types.NewProviderConsAddress([]byte("providerAddr")) // trying to opt in to a non-proposed and non-registered chain returns an error - require.Error(t, providerKeeper.HandleOptIn(ctx, "unknownChainID", providerAddr, nil)) + require.Error(t, providerKeeper.HandleOptIn(ctx, "unknownChainID", providerAddr, "")) providerKeeper.SetProposedConsumerChain(ctx, "chainID", 1) require.False(t, providerKeeper.IsOptedIn(ctx, "chainID", providerAddr)) - providerKeeper.HandleOptIn(ctx, "chainID", providerAddr, nil) + providerKeeper.HandleOptIn(ctx, "chainID", providerAddr, "") require.True(t, providerKeeper.IsOptedIn(ctx, "chainID", providerAddr)) } @@ -71,7 +71,7 @@ func TestHandleOptInWithConsumerKey(t *testing.T) { consumerKey := "{\"@type\":\"/cosmos.crypto.ed25519.PubKey\",\"key\":\"Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=\"}" expectedConsumerPubKey, _ := providerKeeper.ParseConsumerKey(consumerKey) - err := providerKeeper.HandleOptIn(ctx, "chainID", providerAddr, &consumerKey) + err := providerKeeper.HandleOptIn(ctx, "chainID", providerAddr, consumerKey) require.NoError(t, err) // assert that the consumeKey was assigned to `providerAddr` validator on chain with id `chainID` @@ -299,56 +299,55 @@ func TestComputeMinPowerToOptIn(t *testing.T) { createStakingValidator(ctx, mocks, 5, 6), } - m, err := providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 100) + m, err := providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 100) require.NoError(t, err) require.Equal(t, int64(1), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 97) + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 97) require.NoError(t, err) require.Equal(t, int64(1), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 96) + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 96) require.NoError(t, err) require.Equal(t, int64(3), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 85) + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 85) require.NoError(t, err) require.Equal(t, int64(3), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 84) + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 84) require.NoError(t, err) require.Equal(t, int64(5), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 65) + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 65) require.NoError(t, err) require.Equal(t, int64(5), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 64) + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 64) require.NoError(t, err) require.Equal(t, int64(6), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 41) + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 50) require.NoError(t, err) require.Equal(t, int64(6), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 40) + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 40) require.NoError(t, err) require.Equal(t, int64(10), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 1) + m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 1) require.NoError(t, err) require.Equal(t, int64(10), m) - // exceptional case when we erroneously call with `topN == 0` or `topN > 100` - _, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 0) + _, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 0) require.Error(t, err) - _, err = providerKeeper.ComputeMinPowerToOptIn(ctx, "chainID", bondedValidators, 101) + _, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 101) require.Error(t, err) } -// TestFilterOptedInAndAllowAndDenylistedPredicate returns true if `validator` is opted in, in `chainID. -func TestFilterOptedInAndAllowAndDenylistedPredicate(t *testing.T) { +// TestCanValidateChain returns true if `validator` is opted in, in `chainID. +func TestCanValidateChain(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() @@ -357,24 +356,24 @@ func TestFilterOptedInAndAllowAndDenylistedPredicate(t *testing.T) { providerAddr := types.NewProviderConsAddress(consAddr) // with no allowlist or denylist, the validator has to be opted in, in order to consider it - require.False(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) + require.False(t, providerKeeper.CanValidateChain(ctx, "chainID", providerAddr)) providerKeeper.SetOptedIn(ctx, "chainID", types.NewProviderConsAddress(consAddr)) - require.True(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) + require.True(t, providerKeeper.CanValidateChain(ctx, "chainID", providerAddr)) // create an allow list but do not add the validator `providerAddr` to it validatorA := createStakingValidator(ctx, mocks, 1, 1) consAddrA, _ := validatorA.GetConsAddr() providerKeeper.SetAllowlist(ctx, "chainID", types.NewProviderConsAddress(consAddrA)) - require.False(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) + require.False(t, providerKeeper.CanValidateChain(ctx, "chainID", providerAddr)) providerKeeper.SetAllowlist(ctx, "chainID", types.NewProviderConsAddress(consAddr)) - require.True(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) + require.True(t, providerKeeper.CanValidateChain(ctx, "chainID", providerAddr)) // create a denylist but do not add validator `providerAddr` to it providerKeeper.SetDenylist(ctx, "chainID", types.NewProviderConsAddress(consAddrA)) - require.True(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) + require.True(t, providerKeeper.CanValidateChain(ctx, "chainID", providerAddr)) // add validator `providerAddr` to the denylist providerKeeper.SetDenylist(ctx, "chainID", types.NewProviderConsAddress(consAddr)) - require.False(t, providerKeeper.FilterOptedInAndAllowAndDenylistedPredicate(ctx, "chainID", providerAddr)) + require.False(t, providerKeeper.CanValidateChain(ctx, "chainID", providerAddr)) } func TestCapValidatorSet(t *testing.T) { @@ -536,7 +535,7 @@ func noMoreThanPercent(validators []types.ConsumerValidator, percent uint32) boo } for _, v := range validators { - if (float64(v.Power)/float64(sum))*100.0 > float64(percent) { + if float64(v.Power)*100.0 > float64(percent)*float64(sum) { return false } } diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 15281c797b..651d9b95f5 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -297,9 +297,11 @@ func (k Keeper) MakeConsumerGenesis( if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { // in a Top-N chain, we automatically opt in all validators that belong to the top N - minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, bondedValidators, prop.Top_N) + minPower, err := k.ComputeMinPowerToOptIn(ctx, bondedValidators, prop.Top_N) if err == nil { k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) + } else { + return gen, nil, err } } diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index 230ed0a96a..96cca06bfc 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -223,11 +223,14 @@ func (k Keeper) QueueVSCPackets(ctx sdk.Context) { for _, chain := range k.GetAllConsumerChains(ctx) { currentValidators := k.GetConsumerValSet(ctx, chain.ChainId) - if topN, found := k.GetTopN(ctx, chain.ChainId); found && topN > 0 { + if chain.Top_N > 0 { // in a Top-N chain, we automatically opt in all validators that belong to the top N - minPower, err := k.ComputeMinPowerToOptIn(ctx, chain.ChainId, bondedValidators, topN) + minPower, err := k.ComputeMinPowerToOptIn(ctx, bondedValidators, chain.Top_N) if err == nil { k.OptInTopNValidators(ctx, chain.ChainId, bondedValidators, minPower) + } else { + // we just log here and do not panic because panic-ing would halt the provider chain + k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chain.ChainId, "error", err) } } diff --git a/x/ccv/provider/keeper/validator_set_update.go b/x/ccv/provider/keeper/validator_set_update.go index 264fdd67f2..7fa175e1b9 100644 --- a/x/ccv/provider/keeper/validator_set_update.go +++ b/x/ccv/provider/keeper/validator_set_update.go @@ -101,12 +101,12 @@ func DiffValidators( ) []abci.ValidatorUpdate { var updates []abci.ValidatorUpdate - isCurrentValidator := make(map[string]types.ConsumerValidator) + isCurrentValidator := make(map[string]types.ConsumerValidator, len(currentValidators)) for _, val := range currentValidators { isCurrentValidator[val.ConsumerPublicKey.String()] = val } - isNextValidator := make(map[string]types.ConsumerValidator) + isNextValidator := make(map[string]types.ConsumerValidator, len(nextValidators)) for _, val := range nextValidators { isNextValidator[val.ConsumerPublicKey.String()] = val } From 035377e95d6673b71f5cc916eff4b4a7cce9dc29 Mon Sep 17 00:00:00 2001 From: Cosmos SDK <113218068+github-prbot@users.noreply.github.com> Date: Wed, 5 Jun 2024 17:20:03 +0200 Subject: [PATCH 054/102] chore: fix spelling errors (#1939) chore: spelling errors fixes Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com> --- docs/docs/adrs/adr-017-allowing-inactive-validators.md | 2 +- x/ccv/provider/keeper/proposal.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/adrs/adr-017-allowing-inactive-validators.md b/docs/docs/adrs/adr-017-allowing-inactive-validators.md index 9a3715f135..f76f5f7069 100644 --- a/docs/docs/adrs/adr-017-allowing-inactive-validators.md +++ b/docs/docs/adrs/adr-017-allowing-inactive-validators.md @@ -67,7 +67,7 @@ set of consumer chains. To mitigate risks from validators with little stake, we introduce a minimum stake requirement for validators to be able to validate on consumer chains, which can be set by each consumer chain independently, with a default value set by the provider chain. -Additionally, we indepdently allow individual consumer chains to disable this feature, which will disallow validators from outside the provider active set from validating on the consumer chain and revert them to the previous behaviour of only considering validators of the provider that are part of the active consensus validator set. +Additionally, we independently allow individual consumer chains to disable this feature, which will disallow validators from outside the provider active set from validating on the consumer chain and revert them to the previous behaviour of only considering validators of the provider that are part of the active consensus validator set. Additional risk mitigations are to increase the active set size slowly, and to monitor the effects on the network closely. For the first iteration, we propose to increase the active set size to 200 validators (while keeping the consensus validators to 180), thus letting the 20 validators with the most stake outside of the active set validate on consumer chains. diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 651d9b95f5..34bb5b6971 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -147,7 +147,7 @@ func (k Keeper) HandleConsumerRemovalProposal(ctx sdk.Context, p *types.Consumer // HandleConsumerModificationProposal modifies a running consumer chain func (k Keeper) HandleConsumerModificationProposal(ctx sdk.Context, p *types.ConsumerModificationProposal) error { if _, found := k.GetConsumerClientId(ctx, p.ChainId); !found { - return fmt.Errorf("consumer chain (%s) is not runnig", p.ChainId) + return fmt.Errorf("consumer chain (%s) is not running", p.ChainId) } k.SetTopN(ctx, p.ChainId, p.Top_N) From 42a60bf87238d8425a5f2b163b5940436938ed51 Mon Sep 17 00:00:00 2001 From: Marius Poke Date: Fri, 7 Jun 2024 16:13:15 +0200 Subject: [PATCH 055/102] fix!: Replace GetAllConsumerChains with lightweight version (#1946) * add GetAllConsumerChainIDs * replace GetAllConsumerChains with GetAllRegisteredConsumerChainIDs * add changelog entry * move HasToValidate to grpc_query.go as it's used only there * apply review suggestions --- .../provider/1946-get-consumer-chains.md | 3 + tests/mbt/driver/core.go | 31 +++--- tests/mbt/driver/mbt_test.go | 72 ++++++------ x/ccv/provider/keeper/distribution.go | 10 +- x/ccv/provider/keeper/genesis.go | 36 +++--- x/ccv/provider/keeper/grpc_query.go | 103 ++++++++++++++++-- x/ccv/provider/keeper/grpc_query_test.go | 97 ++++++++++++++++- x/ccv/provider/keeper/hooks.go | 6 +- x/ccv/provider/keeper/keeper.go | 92 ++-------------- x/ccv/provider/keeper/keeper_test.go | 91 ++-------------- x/ccv/provider/keeper/relay.go | 31 +++--- x/ccv/provider/migrations/v3/migrations.go | 8 +- x/ccv/provider/migrations/v5/migrations.go | 7 +- 13 files changed, 306 insertions(+), 281 deletions(-) create mode 100644 .changelog/unreleased/bug-fixes/provider/1946-get-consumer-chains.md diff --git a/.changelog/unreleased/bug-fixes/provider/1946-get-consumer-chains.md b/.changelog/unreleased/bug-fixes/provider/1946-get-consumer-chains.md new file mode 100644 index 0000000000..eae373c390 --- /dev/null +++ b/.changelog/unreleased/bug-fixes/provider/1946-get-consumer-chains.md @@ -0,0 +1,3 @@ +- Replace `GetAllConsumerChains` with lightweight version + (`GetAllRegisteredConsumerChainIDs`) that doesn't call into the staking module + ([\#1946](https://github.com/cosmos/interchain-security/pull/1946)) \ No newline at end of file diff --git a/tests/mbt/driver/core.go b/tests/mbt/driver/core.go index 3c985cb6fa..422e282d2d 100644 --- a/tests/mbt/driver/core.go +++ b/tests/mbt/driver/core.go @@ -30,7 +30,6 @@ import ( consumerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/consumer/keeper" consumertypes "github.com/cosmos/interchain-security/v4/x/ccv/consumer/types" providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" - providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" "github.com/cosmos/interchain-security/v4/x/ccv/types" ) @@ -219,11 +218,7 @@ func (s *Driver) getStateString() string { state.WriteString("\n") state.WriteString("Consumers Chains:\n") - consumerChains := s.providerKeeper().GetAllConsumerChains(s.providerCtx()) - chainIds := make([]string, len(consumerChains)) - for i, consumerChain := range consumerChains { - chainIds[i] = consumerChain.ChainId - } + chainIds := s.providerKeeper().GetAllRegisteredConsumerChainIDs(s.providerCtx()) state.WriteString(strings.Join(chainIds, ", ")) state.WriteString("\n\n") @@ -261,11 +256,11 @@ func (s *Driver) getChainStateString(chain ChainId) string { if !s.isProviderChain(chain) { // Check whether the chain is in the consumer chains on the provider - consumerChains := s.providerKeeper().GetAllConsumerChains(s.providerCtx()) + consumerChainIDs := s.providerKeeper().GetAllRegisteredConsumerChainIDs(s.providerCtx()) found := false - for _, consumerChain := range consumerChains { - if consumerChain.ChainId == string(chain) { + for _, consumerChainID := range consumerChainIDs { + if consumerChainID == string(chain) { found = true } } @@ -369,16 +364,16 @@ func (s *Driver) endAndBeginBlock(chain ChainId, timeAdvancement time.Duration) return header } -func (s *Driver) runningConsumers() []providertypes.Chain { - consumersOnProvider := s.providerKeeper().GetAllConsumerChains(s.providerCtx()) +func (s *Driver) runningConsumerChainIDs() []ChainId { + consumerIDsOnProvider := s.providerKeeper().GetAllRegisteredConsumerChainIDs(s.providerCtx()) - consumersWithIntactChannel := make([]providertypes.Chain, 0) - for _, consumer := range consumersOnProvider { - if s.path(ChainId(consumer.ChainId)).Path.EndpointA.GetChannel().State == channeltypes.CLOSED || - s.path(ChainId(consumer.ChainId)).Path.EndpointB.GetChannel().State == channeltypes.CLOSED { + consumersWithIntactChannel := make([]ChainId, 0) + for _, consumerChainID := range consumerIDsOnProvider { + if s.path(ChainId(consumerChainID)).Path.EndpointA.GetChannel().State == channeltypes.CLOSED || + s.path(ChainId(consumerChainID)).Path.EndpointB.GetChannel().State == channeltypes.CLOSED { continue } - consumersWithIntactChannel = append(consumersWithIntactChannel, consumer) + consumersWithIntactChannel = append(consumersWithIntactChannel, ChainId(consumerChainID)) } return consumersWithIntactChannel } @@ -447,8 +442,8 @@ func (s *Driver) RequestSlash( // DeliverAcks delivers, for each path, // all possible acks (up to math.MaxInt many per path). func (s *Driver) DeliverAcks() { - for _, chain := range s.runningConsumers() { - path := s.path(ChainId(chain.ChainId)) + for _, chainID := range s.runningConsumerChainIDs() { + path := s.path(chainID) path.DeliverAcks(path.Path.EndpointA.Chain.ChainID, math.MaxInt) path.DeliverAcks(path.Path.EndpointB.Chain.ChainID, math.MaxInt) } diff --git a/tests/mbt/driver/mbt_test.go b/tests/mbt/driver/mbt_test.go index 9a82bd7b9b..2acbd4a8f4 100644 --- a/tests/mbt/driver/mbt_test.go +++ b/tests/mbt/driver/mbt_test.go @@ -304,21 +304,21 @@ func RunItfTrace(t *testing.T, path string) { // needs a header of height H+1 to accept the packet // so, we do two blocks, one with a very small increment, // and then another to increment the rest of the time - runningConsumersBefore := driver.runningConsumers() + runningConsumerChainIDsBefore := driver.runningConsumerChainIDs() driver.endAndBeginBlock("provider", 1*time.Nanosecond) - for _, consumer := range driver.runningConsumers() { - UpdateProviderClientOnConsumer(t, driver, consumer.ChainId) + for _, consumerChainID := range driver.runningConsumerChainIDs() { + UpdateProviderClientOnConsumer(t, driver, string(consumerChainID)) } driver.endAndBeginBlock("provider", time.Duration(timeAdvancement)*time.Second-1*time.Nanosecond) - runningConsumersAfter := driver.runningConsumers() + runningConsumerChainIDsAfter := driver.runningConsumerChainIDs() // the consumers that were running before but not after must have timed out - for _, consumer := range runningConsumersBefore { + for _, consumerChainID := range runningConsumerChainIDsBefore { found := false - for _, consumerAfter := range runningConsumersAfter { - if consumerAfter.ChainId == consumer.ChainId { + for _, consumerChainIDAfter := range runningConsumerChainIDsAfter { + if consumerChainIDAfter == consumerChainID { found = true break } @@ -332,8 +332,8 @@ func RunItfTrace(t *testing.T, path string) { // because setting up chains will modify timestamps // when the coordinator is starting chains lastTimestamps := make(map[ChainId]time.Time, len(consumers)) - for _, consumer := range driver.runningConsumers() { - lastTimestamps[ChainId(consumer.ChainId)] = driver.runningTime(ChainId(consumer.ChainId)) + for _, consumerChainID := range driver.runningConsumerChainIDs() { + lastTimestamps[consumerChainID] = driver.runningTime(consumerChainID) } driver.coordinator.CurrentTime = driver.runningTime("provider") @@ -364,12 +364,12 @@ func RunItfTrace(t *testing.T, path string) { // for all connected consumers, update the clients... // unless it was the last consumer to be started, in which case it already has the header // as we called driver.setupConsumer - for _, consumer := range driver.runningConsumers() { - if len(consumersToStart) > 0 && consumer.ChainId == consumersToStart[len(consumersToStart)-1].Value.(string) { + for _, consumerChainID := range driver.runningConsumerChainIDs() { + if len(consumersToStart) > 0 && string(consumerChainID) == consumersToStart[len(consumersToStart)-1].Value.(string) { continue } - UpdateProviderClientOnConsumer(t, driver, consumer.ChainId) + UpdateProviderClientOnConsumer(t, driver, string(consumerChainID)) } case "EndAndBeginBlockForConsumer": @@ -490,33 +490,33 @@ func RunItfTrace(t *testing.T, path string) { t.Logf("Comparing model state to actual state...") // compare the running consumers - modelRunningConsumers := RunningConsumers(currentModelState) + modelRunningConsumerChainIDs := RunningConsumers(currentModelState) - systemRunningConsumers := driver.runningConsumers() - actualRunningConsumers := make([]string, len(systemRunningConsumers)) - for i, chain := range systemRunningConsumers { - actualRunningConsumers[i] = chain.ChainId + systemRunningConsumerChainIDs := driver.runningConsumerChainIDs() + actualRunningConsumerChainIDs := make([]string, len(systemRunningConsumerChainIDs)) + for i, chainID := range systemRunningConsumerChainIDs { + actualRunningConsumerChainIDs[i] = string(chainID) } // sort the slices so that we can compare them - sort.Slice(modelRunningConsumers, func(i, j int) bool { - return modelRunningConsumers[i] < modelRunningConsumers[j] + sort.Slice(modelRunningConsumerChainIDs, func(i, j int) bool { + return modelRunningConsumerChainIDs[i] < modelRunningConsumerChainIDs[j] }) - sort.Slice(actualRunningConsumers, func(i, j int) bool { - return actualRunningConsumers[i] < actualRunningConsumers[j] + sort.Slice(actualRunningConsumerChainIDs, func(i, j int) bool { + return actualRunningConsumerChainIDs[i] < actualRunningConsumerChainIDs[j] }) - require.Equal(t, modelRunningConsumers, actualRunningConsumers, "Running consumers do not match") + require.Equal(t, modelRunningConsumerChainIDs, actualRunningConsumerChainIDs, "Running consumers do not match") // check validator sets - provider current validator set should be the one from the staking keeper - CompareValidatorSets(t, driver, currentModelState, actualRunningConsumers, realAddrsToModelConsAddrs) + CompareValidatorSets(t, driver, currentModelState, actualRunningConsumerChainIDs, realAddrsToModelConsAddrs) // check times - sanity check that the block times match the ones from the model - CompareTimes(driver, actualRunningConsumers, currentModelState, timeOffset) + CompareTimes(driver, actualRunningConsumerChainIDs, currentModelState, timeOffset) // check sent packets: we check that the package queues in the model and the system have the same length. - for _, consumer := range actualRunningConsumers { - ComparePacketQueues(t, driver, currentModelState, consumer, timeOffset) + for _, consumerChainID := range actualRunningConsumerChainIDs { + ComparePacketQueues(t, driver, currentModelState, consumerChainID, timeOffset) } // compare that the sent packets on the proider match the model CompareSentPacketsOnProvider(driver, currentModelState, timeOffset) @@ -526,8 +526,8 @@ func RunItfTrace(t *testing.T, path string) { CompareJailedValidators(driver, currentModelState, timeOffset, addressMap) // for all newly sent vsc packets, figure out which vsc id in the model they correspond to - for _, consumer := range actualRunningConsumers { - actualPackets := driver.packetQueue(PROVIDER, ChainId(consumer)) + for _, consumerChainID := range actualRunningConsumerChainIDs { + actualPackets := driver.packetQueue(PROVIDER, ChainId(consumerChainID)) actualNewPackets := make([]types.ValidatorSetChangePacketData, 0) for _, packet := range actualPackets { @@ -543,7 +543,7 @@ func RunItfTrace(t *testing.T, path string) { actualNewPackets = append(actualNewPackets, packetData) } - modelPackets := PacketQueue(currentModelState, PROVIDER, consumer) + modelPackets := PacketQueue(currentModelState, PROVIDER, consumerChainID) newModelVscIds := make([]uint64, 0) for _, packet := range modelPackets { modelVscId := uint64(packet.Value.(itf.MapExprType)["value"].Value.(itf.MapExprType)["id"].Value.(int64)) @@ -781,15 +781,15 @@ func CompareValSet(modelValSet map[string]itf.Expr, systemValSet map[string]int6 } func CompareSentPacketsOnProvider(driver *Driver, currentModelState map[string]itf.Expr, timeOffset time.Time) { - for _, consumer := range driver.runningConsumers() { - vscSendTimestamps := driver.providerKeeper().GetAllVscSendTimestamps(driver.providerCtx(), consumer.ChainId) + for _, consumerChainID := range driver.runningConsumerChainIDs() { + vscSendTimestamps := driver.providerKeeper().GetAllVscSendTimestamps(driver.providerCtx(), string(consumerChainID)) actualVscSendTimestamps := make([]time.Time, 0) for _, vscSendTimestamp := range vscSendTimestamps { actualVscSendTimestamps = append(actualVscSendTimestamps, vscSendTimestamp.Timestamp) } - modelVscSendTimestamps := VscSendTimestamps(currentModelState, consumer.ChainId) + modelVscSendTimestamps := VscSendTimestamps(currentModelState, string(consumerChainID)) for i, modelVscSendTimestamp := range modelVscSendTimestamps { actualTimeWithOffset := actualVscSendTimestamps[i].Unix() - timeOffset.Unix() @@ -798,7 +798,7 @@ func CompareSentPacketsOnProvider(driver *Driver, currentModelState map[string]i modelVscSendTimestamp, actualTimeWithOffset, "Vsc send timestamps do not match for consumer %v", - consumer.ChainId, + consumerChainID, ) } } @@ -852,9 +852,9 @@ func (s *Stats) EnterStats(driver *Driver) { // max number of in-flight packets inFlightPackets := 0 - for _, consumer := range driver.runningConsumers() { - inFlightPackets += len(driver.packetQueue(PROVIDER, ChainId(consumer.ChainId))) - inFlightPackets += len(driver.packetQueue(ChainId(consumer.ChainId), PROVIDER)) + for _, consumerChainID := range driver.runningConsumerChainIDs() { + inFlightPackets += len(driver.packetQueue(PROVIDER, consumerChainID)) + inFlightPackets += len(driver.packetQueue(consumerChainID, PROVIDER)) } if inFlightPackets > s.maxNumInFlightPackets { s.maxNumInFlightPackets = inFlightPackets diff --git a/x/ccv/provider/keeper/distribution.go b/x/ccv/provider/keeper/distribution.go index f59f111301..7d875b7c38 100644 --- a/x/ccv/provider/keeper/distribution.go +++ b/x/ccv/provider/keeper/distribution.go @@ -76,14 +76,14 @@ func (k Keeper) AllocateTokens(ctx sdk.Context) { } // Iterate over all registered consumer chains - for _, consumer := range k.GetAllConsumerChains(ctx) { + for _, consumerChainID := range k.GetAllRegisteredConsumerChainIDs(ctx) { // transfer the consumer rewards to the distribution module account // note that the rewards transferred are only consumer whitelisted denoms - rewardsCollected, err := k.TransferConsumerRewardsToDistributionModule(ctx, consumer.ChainId) + rewardsCollected, err := k.TransferConsumerRewardsToDistributionModule(ctx, consumerChainID) if err != nil { k.Logger(ctx).Error( "fail to transfer rewards to distribution module for chain %s: %s", - consumer.ChainId, + consumerChainID, err, ) continue @@ -101,7 +101,7 @@ func (k Keeper) AllocateTokens(ctx sdk.Context) { // temporary workaround to keep CanWithdrawInvariant happy // general discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634 feePool := k.distributionKeeper.GetFeePool(ctx) - if k.ComputeConsumerTotalVotingPower(ctx, consumer.ChainId) == 0 { + if k.ComputeConsumerTotalVotingPower(ctx, consumerChainID) == 0 { feePool.CommunityPool = feePool.CommunityPool.Add(rewardsCollectedDec...) k.distributionKeeper.SetFeePool(ctx, feePool) return @@ -116,7 +116,7 @@ func (k Keeper) AllocateTokens(ctx sdk.Context) { // allocate tokens to consumer validators feeAllocated := k.AllocateTokensToConsumerValidators( ctx, - consumer.ChainId, + consumerChainID, feeMultiplier, ) remaining = remaining.Sub(feeAllocated) diff --git a/x/ccv/provider/keeper/genesis.go b/x/ccv/provider/keeper/genesis.go index 2075ff48ae..cc8bdc41cd 100644 --- a/x/ccv/provider/keeper/genesis.go +++ b/x/ccv/provider/keeper/genesis.go @@ -108,47 +108,51 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { // ExportGenesis returns the CCV provider module's exported genesis func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { // get a list of all registered consumer chains - registeredChains := k.GetAllConsumerChains(ctx) + registeredChainIDs := k.GetAllRegisteredConsumerChainIDs(ctx) var exportedVscSendTimestamps []types.ExportedVscSendTimestamp // export states for each consumer chains var consumerStates []types.ConsumerState - for _, chain := range registeredChains { - gen, found := k.GetConsumerGenesis(ctx, chain.ChainId) + for _, chainID := range registeredChainIDs { + // no need for the second return value of GetConsumerClientId + // as GetAllRegisteredConsumerChainIDs already iterated through + // the entire prefix range + clientID, _ := k.GetConsumerClientId(ctx, chainID) + gen, found := k.GetConsumerGenesis(ctx, chainID) if !found { - panic(fmt.Errorf("cannot find genesis for consumer chain %s with client %s", chain.ChainId, chain.ClientId)) + panic(fmt.Errorf("cannot find genesis for consumer chain %s with client %s", chainID, clientID)) } // initial consumer chain states cs := types.ConsumerState{ - ChainId: chain.ChainId, - ClientId: chain.ClientId, + ChainId: chainID, + ClientId: clientID, ConsumerGenesis: gen, - UnbondingOpsIndex: k.GetAllUnbondingOpIndexes(ctx, chain.ChainId), + UnbondingOpsIndex: k.GetAllUnbondingOpIndexes(ctx, chainID), } // try to find channel id for the current consumer chain - channelId, found := k.GetChainToChannel(ctx, chain.ChainId) + channelId, found := k.GetChainToChannel(ctx, chainID) if found { cs.ChannelId = channelId - cs.InitialHeight, found = k.GetInitChainHeight(ctx, chain.ChainId) + cs.InitialHeight, found = k.GetInitChainHeight(ctx, chainID) if !found { - panic(fmt.Errorf("cannot find init height for consumer chain %s", chain.ChainId)) + panic(fmt.Errorf("cannot find init height for consumer chain %s", chainID)) } - cs.SlashDowntimeAck = k.GetSlashAcks(ctx, chain.ChainId) + cs.SlashDowntimeAck = k.GetSlashAcks(ctx, chainID) } - cs.PendingValsetChanges = k.GetPendingVSCPackets(ctx, chain.ChainId) + cs.PendingValsetChanges = k.GetPendingVSCPackets(ctx, chainID) consumerStates = append(consumerStates, cs) - vscSendTimestamps := k.GetAllVscSendTimestamps(ctx, chain.ChainId) - exportedVscSendTimestamps = append(exportedVscSendTimestamps, types.ExportedVscSendTimestamp{ChainId: chain.ChainId, VscSendTimestamps: vscSendTimestamps}) + vscSendTimestamps := k.GetAllVscSendTimestamps(ctx, chainID) + exportedVscSendTimestamps = append(exportedVscSendTimestamps, types.ExportedVscSendTimestamp{ChainId: chainID, VscSendTimestamps: vscSendTimestamps}) } // ConsumerAddrsToPrune are added only for registered consumer chains consumerAddrsToPrune := []types.ConsumerAddrsToPrune{} - for _, chain := range registeredChains { - consumerAddrsToPrune = append(consumerAddrsToPrune, k.GetAllConsumerAddrsToPrune(ctx, chain.ChainId)...) + for _, chainID := range registeredChainIDs { + consumerAddrsToPrune = append(consumerAddrsToPrune, k.GetAllConsumerAddrsToPrune(ctx, chainID)...) } params := k.GetParams(ctx) diff --git a/x/ccv/provider/keeper/grpc_query.go b/x/ccv/provider/keeper/grpc_query.go index 019c1dc957..ee00556f06 100644 --- a/x/ccv/provider/keeper/grpc_query.go +++ b/x/ccv/provider/keeper/grpc_query.go @@ -47,15 +47,66 @@ func (k Keeper) QueryConsumerChains(goCtx context.Context, req *types.QueryConsu ctx := sdk.UnwrapSDKContext(goCtx) chains := []*types.Chain{} - for _, chain := range k.GetAllConsumerChains(ctx) { - // prevent implicit memory aliasing - c := chain + for _, chainID := range k.GetAllRegisteredConsumerChainIDs(ctx) { + c, err := k.GetConsumerChain(ctx, chainID) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } chains = append(chains, &c) } return &types.QueryConsumerChainsResponse{Chains: chains}, nil } +// GetConsumerChain returns a Chain data structure with all the necessary fields +func (k Keeper) GetConsumerChain(ctx sdk.Context, chainID string) (types.Chain, error) { + clientID, found := k.GetConsumerClientId(ctx, chainID) + if !found { + return types.Chain{}, fmt.Errorf("cannot find clientID for consumer (%s)", chainID) + } + + topN, found := k.GetTopN(ctx, chainID) + + // Get MinPowerInTop_N + var minPowerInTopN int64 + if found && topN > 0 { + res, err := k.ComputeMinPowerToOptIn(ctx, k.stakingKeeper.GetLastValidators(ctx), topN) + if err != nil { + return types.Chain{}, fmt.Errorf("failed to compute min power to opt in for chain (%s): %w", chainID, err) + } + minPowerInTopN = res + } else { + minPowerInTopN = -1 + } + + validatorSetCap, _ := k.GetValidatorSetCap(ctx, chainID) + + validatorsPowerCap, _ := k.GetValidatorsPowerCap(ctx, chainID) + + allowlist := k.GetAllowList(ctx, chainID) + strAllowlist := make([]string, len(allowlist)) + for i, addr := range allowlist { + strAllowlist[i] = addr.String() + } + + denylist := k.GetDenyList(ctx, chainID) + strDenylist := make([]string, len(denylist)) + for i, addr := range denylist { + strDenylist[i] = addr.String() + } + + return types.Chain{ + ChainId: chainID, + ClientId: clientID, + Top_N: topN, + MinPowerInTop_N: minPowerInTopN, + ValidatorSetCap: validatorSetCap, + ValidatorsPowerCap: validatorsPowerCap, + Allowlist: strAllowlist, + Denylist: strDenylist, + }, nil +} + func (k Keeper) QueryConsumerChainStarts(goCtx context.Context, req *types.QueryConsumerChainStartProposalsRequest) (*types.QueryConsumerChainStartProposalsResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "empty request") @@ -307,11 +358,9 @@ func (k Keeper) QueryConsumerChainsValidatorHasToValidate(goCtx context.Context, // get all the consumer chains for which the validator is either already // opted-in, currently a consumer validator or if its voting power is within the TopN validators consumersToValidate := []string{} - for _, consumer := range k.GetAllConsumerChains(ctx) { - chainID := consumer.ChainId - - if hasToValidate, err := k.HasToValidate(ctx, provAddr, chainID); err == nil && hasToValidate { - consumersToValidate = append(consumersToValidate, chainID) + for _, consumerChainID := range k.GetAllRegisteredConsumerChainIDs(ctx) { + if hasToValidate, err := k.hasToValidate(ctx, provAddr, consumerChainID); err == nil && hasToValidate { + consumersToValidate = append(consumersToValidate, consumerChainID) } } @@ -320,6 +369,44 @@ func (k Keeper) QueryConsumerChainsValidatorHasToValidate(goCtx context.Context, }, nil } +// hasToValidate checks if a validator needs to validate on a consumer chain +func (k Keeper) hasToValidate( + ctx sdk.Context, + provAddr types.ProviderConsAddress, + chainID string, +) (bool, error) { + // if the validator was sent as part of the packet in the last epoch, it has to validate + if k.IsConsumerValidator(ctx, chainID, provAddr) { + return true, nil + } + + // if the validator was not part of the last epoch, check if the validator is going to be part of te next epoch + bondedValidators := k.stakingKeeper.GetLastValidators(ctx) + if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { + // in a Top-N chain, we automatically opt in all validators that belong to the top N + minPower, err := k.ComputeMinPowerToOptIn(ctx, bondedValidators, topN) + if err == nil { + k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) + } else { + k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err) + } + } + + // if the validator is opted in and belongs to the validators of the next epoch, then if nothing changes + // the validator would have to validate in the next epoch + if k.IsOptedIn(ctx, chainID, provAddr) { + nextValidators := k.ComputeNextValidators(ctx, chainID, k.stakingKeeper.GetLastValidators(ctx)) + for _, v := range nextValidators { + consAddr := sdk.ConsAddress(v.ProviderConsAddr) + if provAddr.ToSdkConsAddr().Equals(consAddr) { + return true, nil + } + } + } + + return false, nil +} + // QueryValidatorConsumerCommissionRate returns the commission rate a given // validator charges on a given consumer chain func (k Keeper) QueryValidatorConsumerCommissionRate(goCtx context.Context, req *types.QueryValidatorConsumerCommissionRateRequest) (*types.QueryValidatorConsumerCommissionRateResponse, error) { diff --git a/x/ccv/provider/keeper/grpc_query_test.go b/x/ccv/provider/keeper/grpc_query_test.go index 71a9b82b7e..4f3825238f 100644 --- a/x/ccv/provider/keeper/grpc_query_test.go +++ b/x/ccv/provider/keeper/grpc_query_test.go @@ -1,11 +1,14 @@ package keeper_test import ( - "github.com/cometbft/cometbft/proto/tendermint/crypto" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "fmt" "testing" "time" + "github.com/cometbft/cometbft/proto/tendermint/crypto" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" sdktypes "github.com/cosmos/cosmos-sdk/types" @@ -249,3 +252,93 @@ func TestQueryValidatorConsumerCommissionRate(t *testing.T) { res, _ = pk.QueryValidatorConsumerCommissionRate(ctx, &req) require.Equal(t, expectedCommissionRate, res.Rate) } + +// TestGetConsumerChain tests GetConsumerChain behaviour correctness +func TestGetConsumerChain(t *testing.T) { + pk, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainIDs := []string{"chain-1", "chain-2", "chain-3", "chain-4"} + + // mock the validator set + vals := []stakingtypes.Validator{ + {OperatorAddress: "cosmosvaloper1c4k24jzduc365kywrsvf5ujz4ya6mwympnc4en"}, // 50 power + {OperatorAddress: "cosmosvaloper196ax4vc0lwpxndu9dyhvca7jhxp70rmcvrj90c"}, // 150 power + {OperatorAddress: "cosmosvaloper1clpqr4nrk4khgkxj78fcwwh6dl3uw4epsluffn"}, // 300 power + {OperatorAddress: "cosmosvaloper1tflk30mq5vgqjdly92kkhhq3raev2hnz6eete3"}, // 500 power + } + powers := []int64{50, 150, 300, 500} // sum = 1000 + mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Return(vals).AnyTimes() + + for i, val := range vals { + mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), val.GetOperator()).Return(powers[i]).AnyTimes() + } + + // set Top N parameters, client ids and expected result + topNs := []uint32{0, 70, 90, 100} + expectedMinPowerInTopNs := []int64{ + -1, // Top N is 0, so not a Top N chain + 300, // 500 and 300 are in Top 70% + 150, // 150 is also in the top 90%, + 50, // everyone is in the top 100% + } + + validatorSetCaps := []uint32{0, 5, 10, 20} + validatorPowerCaps := []uint32{0, 5, 10, 33} + allowlists := [][]types.ProviderConsAddress{ + {}, + {types.NewProviderConsAddress([]byte("providerAddr1")), types.NewProviderConsAddress([]byte("providerAddr2"))}, + {types.NewProviderConsAddress([]byte("providerAddr3"))}, + {}, + } + + denylists := [][]types.ProviderConsAddress{ + {types.NewProviderConsAddress([]byte("providerAddr4")), types.NewProviderConsAddress([]byte("providerAddr5"))}, + {}, + {types.NewProviderConsAddress([]byte("providerAddr6"))}, + {}, + } + + expectedGetAllOrder := []types.Chain{} + for i, chainID := range chainIDs { + clientID := fmt.Sprintf("client-%d", len(chainIDs)-i) + topN := topNs[i] + pk.SetConsumerClientId(ctx, chainID, clientID) + pk.SetTopN(ctx, chainID, topN) + pk.SetValidatorSetCap(ctx, chainID, validatorSetCaps[i]) + pk.SetValidatorsPowerCap(ctx, chainID, validatorPowerCaps[i]) + for _, addr := range allowlists[i] { + pk.SetAllowlist(ctx, chainID, addr) + } + for _, addr := range denylists[i] { + pk.SetDenylist(ctx, chainID, addr) + } + strAllowlist := make([]string, len(allowlists[i])) + for j, addr := range allowlists[i] { + strAllowlist[j] = addr.String() + } + + strDenylist := make([]string, len(denylists[i])) + for j, addr := range denylists[i] { + strDenylist[j] = addr.String() + } + + expectedGetAllOrder = append(expectedGetAllOrder, + types.Chain{ + ChainId: chainID, + ClientId: clientID, + Top_N: topN, + MinPowerInTop_N: expectedMinPowerInTopNs[i], + ValidatorSetCap: validatorSetCaps[i], + ValidatorsPowerCap: validatorPowerCaps[i], + Allowlist: strAllowlist, + Denylist: strDenylist, + }) + } + + for i, chainID := range pk.GetAllRegisteredAndProposedChainIDs(ctx) { + c, err := pk.GetConsumerChain(ctx, chainID) + require.NoError(t, err) + require.Equal(t, expectedGetAllOrder[i], c) + } +} diff --git a/x/ccv/provider/keeper/hooks.go b/x/ccv/provider/keeper/hooks.go index ed926f16c7..5aef4a16ef 100644 --- a/x/ccv/provider/keeper/hooks.go +++ b/x/ccv/provider/keeper/hooks.go @@ -90,9 +90,9 @@ func (h Hooks) AfterUnbondingInitiated(ctx sdk.Context, id uint64) error { } // get all consumers where the validator is in the validator set - for _, chain := range h.k.GetAllConsumerChains(ctx) { - if h.k.IsConsumerValidator(ctx, chain.ChainId, types.NewProviderConsAddress(consAddr)) { - consumerChainIDS = append(consumerChainIDS, chain.ChainId) + for _, chainID := range h.k.GetAllRegisteredConsumerChainIDs(ctx) { + if h.k.IsConsumerValidator(ctx, chainID, types.NewProviderConsAddress(consAddr)) { + consumerChainIDS = append(consumerChainIDS, chainID) } } diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 438cb5aa67..9b95036dda 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -237,13 +237,15 @@ func (k Keeper) GetAllPendingConsumerChainIDs(ctx sdk.Context) []string { return chainIDs } -// GetAllConsumerChains gets all of the consumer chains, for which the provider module +// GetAllRegisteredConsumerChainIDs gets all of the consumer chain IDs, for which the provider module // created IBC clients. Consumer chains with created clients are also referred to as registered. // // Note that the registered consumer chains are stored under keys with the following format: // ChainToClientBytePrefix | chainID // Thus, the returned array is in ascending order of chainIDs. -func (k Keeper) GetAllConsumerChains(ctx sdk.Context) (chains []types.Chain) { +func (k Keeper) GetAllRegisteredConsumerChainIDs(ctx sdk.Context) []string { + chainIDs := []string{} + store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte{types.ChainToClientBytePrefix}) defer iterator.Close() @@ -251,50 +253,10 @@ func (k Keeper) GetAllConsumerChains(ctx sdk.Context) (chains []types.Chain) { for ; iterator.Valid(); iterator.Next() { // remove 1 byte prefix from key to retrieve chainID chainID := string(iterator.Key()[1:]) - clientID := string(iterator.Value()) - - topN, found := k.GetTopN(ctx, chainID) - - var minPowerInTopN int64 - if found && topN > 0 { - res, err := k.ComputeMinPowerToOptIn(ctx, k.stakingKeeper.GetLastValidators(ctx), topN) - if err != nil { - k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err) - minPowerInTopN = -1 - } else { - minPowerInTopN = res - } - } else { - minPowerInTopN = -1 - } - - validatorSetCap, _ := k.GetValidatorSetCap(ctx, chainID) - validatorsPowerCap, _ := k.GetValidatorsPowerCap(ctx, chainID) - allowlist := k.GetAllowList(ctx, chainID) - strAllowlist := make([]string, len(allowlist)) - for i, addr := range allowlist { - strAllowlist[i] = addr.String() - } - - denylist := k.GetDenyList(ctx, chainID) - strDenylist := make([]string, len(denylist)) - for i, addr := range denylist { - strDenylist[i] = addr.String() - } - - chains = append(chains, types.Chain{ - ChainId: chainID, - ClientId: clientID, - Top_N: topN, - MinPowerInTop_N: minPowerInTopN, - ValidatorSetCap: validatorSetCap, - ValidatorsPowerCap: validatorsPowerCap, - Allowlist: strAllowlist, - Denylist: strDenylist, - }) + chainIDs = append(chainIDs, chainID) } - return chains + return chainIDs } // SetChannelToChain sets the mapping from the CCV channel ID to the consumer chainID. @@ -1159,10 +1121,7 @@ func (k Keeper) BondDenom(ctx sdk.Context) string { func (k Keeper) GetAllRegisteredAndProposedChainIDs(ctx sdk.Context) []string { allConsumerChains := []string{} - consumerChains := k.GetAllConsumerChains(ctx) - for _, consumerChain := range consumerChains { - allConsumerChains = append(allConsumerChains, consumerChain.ChainId) - } + allConsumerChains = append(allConsumerChains, k.GetAllRegisteredConsumerChainIDs(ctx)...) proposedChains := k.GetAllProposedConsumerChainIDs(ctx) for _, proposedChain := range proposedChains { allConsumerChains = append(allConsumerChains, proposedChain.ChainID) @@ -1284,43 +1243,6 @@ func (k Keeper) DeleteAllOptedIn( } } -func (k Keeper) HasToValidate( - ctx sdk.Context, - provAddr types.ProviderConsAddress, - chainID string, -) (bool, error) { - // if the validator was sent as part of the packet in the last epoch, it has to validate - if k.IsConsumerValidator(ctx, chainID, provAddr) { - return true, nil - } - - // if the validator was not part of the last epoch, check if the validator is going to be part of te next epoch - bondedValidators := k.stakingKeeper.GetLastValidators(ctx) - if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { - // in a Top-N chain, we automatically opt in all validators that belong to the top N - minPower, err := k.ComputeMinPowerToOptIn(ctx, bondedValidators, topN) - if err == nil { - k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) - } else { - k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err) - } - } - - // if the validator is opted in and belongs to the validators of the next epoch, then if nothing changes - // the validator would have to validate in the next epoch - if k.IsOptedIn(ctx, chainID, provAddr) { - nextValidators := k.ComputeNextValidators(ctx, chainID, k.stakingKeeper.GetLastValidators(ctx)) - for _, v := range nextValidators { - consAddr := sdk.ConsAddress(v.ProviderConsAddr) - if provAddr.ToSdkConsAddr().Equals(consAddr) { - return true, nil - } - } - } - - return false, nil -} - // SetConsumerCommissionRate sets a per-consumer chain commission rate // for the given validator address func (k Keeper) SetConsumerCommissionRate( diff --git a/x/ccv/provider/keeper/keeper_test.go b/x/ccv/provider/keeper/keeper_test.go index 894011cfab..f68afc20c2 100644 --- a/x/ccv/provider/keeper/keeper_test.go +++ b/x/ccv/provider/keeper/keeper_test.go @@ -8,7 +8,6 @@ import ( "time" ibctesting "github.com/cosmos/ibc-go/v7/testing" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" @@ -21,8 +20,6 @@ import ( testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" - - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) const consumer = "consumer" @@ -396,96 +393,22 @@ func TestVscSendTimestamp(t *testing.T) { require.Empty(t, providerKeeper.GetAllVscSendTimestamps(ctx, chainID)) } -// TestGetAllConsumerChains tests GetAllConsumerChains behaviour correctness -func TestGetAllConsumerChains(t *testing.T) { - pk, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) +func TestGetAllRegisteredConsumerChainIDs(t *testing.T) { + pk, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() chainIDs := []string{"chain-2", "chain-1", "chain-4", "chain-3"} + // GetAllRegisteredConsumerChainIDs iterates over chainID in lexicographical order + expectedChainIDs := []string{"chain-1", "chain-2", "chain-3", "chain-4"} - // mock the validator set - vals := []stakingtypes.Validator{ - {OperatorAddress: "cosmosvaloper1c4k24jzduc365kywrsvf5ujz4ya6mwympnc4en"}, // 50 power - {OperatorAddress: "cosmosvaloper196ax4vc0lwpxndu9dyhvca7jhxp70rmcvrj90c"}, // 150 power - {OperatorAddress: "cosmosvaloper1clpqr4nrk4khgkxj78fcwwh6dl3uw4epsluffn"}, // 300 power - {OperatorAddress: "cosmosvaloper1tflk30mq5vgqjdly92kkhhq3raev2hnz6eete3"}, // 500 power - } - powers := []int64{50, 150, 300, 500} // sum = 1000 - mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Return(vals).AnyTimes() - - for i, val := range vals { - mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), val.GetOperator()).Return(powers[i]).AnyTimes() - } - - // set Top N parameters, client ids and expected result - topNs := []uint32{0, 70, 90, 100} - expectedMinPowerInTopNs := []int64{ - -1, // Top N is 0, so not a Top N chain - 300, // 500 and 300 are in Top 70% - 150, // 150 is also in the top 90%, - 50, // everyone is in the top 100% - } - - validatorSetCaps := []uint32{0, 5, 10, 20} - validatorPowerCaps := []uint32{0, 5, 10, 33} - allowlists := [][]types.ProviderConsAddress{ - {}, - {types.NewProviderConsAddress([]byte("providerAddr1")), types.NewProviderConsAddress([]byte("providerAddr2"))}, - {types.NewProviderConsAddress([]byte("providerAddr3"))}, - {}, - } - - denylists := [][]types.ProviderConsAddress{ - {types.NewProviderConsAddress([]byte("providerAddr4")), types.NewProviderConsAddress([]byte("providerAddr5"))}, - {}, - {types.NewProviderConsAddress([]byte("providerAddr6"))}, - {}, - } - - expectedGetAllOrder := []types.Chain{} for i, chainID := range chainIDs { clientID := fmt.Sprintf("client-%d", len(chainIDs)-i) - topN := topNs[i] pk.SetConsumerClientId(ctx, chainID, clientID) - pk.SetTopN(ctx, chainID, topN) - pk.SetValidatorSetCap(ctx, chainID, validatorSetCaps[i]) - pk.SetValidatorsPowerCap(ctx, chainID, validatorPowerCaps[i]) - for _, addr := range allowlists[i] { - pk.SetAllowlist(ctx, chainID, addr) - } - for _, addr := range denylists[i] { - pk.SetDenylist(ctx, chainID, addr) - } - strAllowlist := make([]string, len(allowlists[i])) - for j, addr := range allowlists[i] { - strAllowlist[j] = addr.String() - } - - strDenylist := make([]string, len(denylists[i])) - for j, addr := range denylists[i] { - strDenylist[j] = addr.String() - } - - expectedGetAllOrder = append(expectedGetAllOrder, - types.Chain{ - ChainId: chainID, - ClientId: clientID, - Top_N: topN, - MinPowerInTop_N: expectedMinPowerInTopNs[i], - ValidatorSetCap: validatorSetCaps[i], - ValidatorsPowerCap: validatorPowerCaps[i], - Allowlist: strAllowlist, - Denylist: strDenylist, - }) - } - // sorting by chainID - sort.Slice(expectedGetAllOrder, func(i, j int) bool { - return expectedGetAllOrder[i].ChainId < expectedGetAllOrder[j].ChainId - }) + } - result := pk.GetAllConsumerChains(ctx) + result := pk.GetAllRegisteredConsumerChainIDs(ctx) require.Len(t, result, len(chainIDs)) - require.Equal(t, expectedGetAllOrder, result) + require.Equal(t, expectedChainIDs, result) } // TestGetAllChannelToChains tests GetAllChannelToChains behaviour correctness diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index 96cca06bfc..852b70928b 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -166,10 +166,10 @@ func (k Keeper) EndBlockVSU(ctx sdk.Context) { // If the CCV channel is not established for a consumer chain, // the updates will remain queued until the channel is established func (k Keeper) SendVSCPackets(ctx sdk.Context) { - for _, chain := range k.GetAllConsumerChains(ctx) { + for _, chainID := range k.GetAllRegisteredConsumerChainIDs(ctx) { // check if CCV channel is established and send - if channelID, found := k.GetChainToChannel(ctx, chain.ChainId); found { - k.SendVSCPacketsToChain(ctx, chain.ChainId, channelID) + if channelID, found := k.GetChainToChannel(ctx, chainID); found { + k.SendVSCPacketsToChain(ctx, chainID, channelID) } } } @@ -220,35 +220,36 @@ func (k Keeper) QueueVSCPackets(ctx sdk.Context) { // get the bonded validators from the staking module bondedValidators := k.stakingKeeper.GetLastValidators(ctx) - for _, chain := range k.GetAllConsumerChains(ctx) { - currentValidators := k.GetConsumerValSet(ctx, chain.ChainId) + for _, chainID := range k.GetAllRegisteredConsumerChainIDs(ctx) { + currentValidators := k.GetConsumerValSet(ctx, chainID) + topN, _ := k.GetTopN(ctx, chainID) - if chain.Top_N > 0 { + if topN > 0 { // in a Top-N chain, we automatically opt in all validators that belong to the top N - minPower, err := k.ComputeMinPowerToOptIn(ctx, bondedValidators, chain.Top_N) + minPower, err := k.ComputeMinPowerToOptIn(ctx, bondedValidators, topN) if err == nil { - k.OptInTopNValidators(ctx, chain.ChainId, bondedValidators, minPower) + k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) } else { // we just log here and do not panic because panic-ing would halt the provider chain - k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chain.ChainId, "error", err) + k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err) } } - nextValidators := k.ComputeNextValidators(ctx, chain.ChainId, bondedValidators) + nextValidators := k.ComputeNextValidators(ctx, chainID, bondedValidators) valUpdates := DiffValidators(currentValidators, nextValidators) - k.SetConsumerValSet(ctx, chain.ChainId, nextValidators) + k.SetConsumerValSet(ctx, chainID, nextValidators) // check whether there are changes in the validator set; // note that this also entails unbonding operations // w/o changes in the voting power of the validators in the validator set - unbondingOps := k.GetUnbondingOpsFromIndex(ctx, chain.ChainId, valUpdateID) + unbondingOps := k.GetUnbondingOpsFromIndex(ctx, chainID, valUpdateID) if len(valUpdates) != 0 || len(unbondingOps) != 0 { // construct validator set change packet data - packet := ccv.NewValidatorSetChangePacketData(valUpdates, valUpdateID, k.ConsumeSlashAcks(ctx, chain.ChainId)) - k.AppendPendingVSCPackets(ctx, chain.ChainId, packet) + packet := ccv.NewValidatorSetChangePacketData(valUpdates, valUpdateID, k.ConsumeSlashAcks(ctx, chainID)) + k.AppendPendingVSCPackets(ctx, chainID, packet) k.Logger(ctx).Info("VSCPacket enqueued:", - "chainID", chain.ChainId, + "chainID", chainID, "vscID", valUpdateID, "len updates", len(valUpdates), "len unbonding ops", len(unbondingOps), diff --git a/x/ccv/provider/migrations/v3/migrations.go b/x/ccv/provider/migrations/v3/migrations.go index d308316761..0a3ae68f12 100644 --- a/x/ccv/provider/migrations/v3/migrations.go +++ b/x/ccv/provider/migrations/v3/migrations.go @@ -11,15 +11,15 @@ import ( // MigrateQueuedPackets processes all queued packet data for all consumer chains that were stored // on the provider in the v2 consensus version (jail throttling v1). func MigrateQueuedPackets(ctx sdk.Context, k providerkeeper.Keeper) error { - for _, consumer := range k.GetAllConsumerChains(ctx) { - slashData, vscmData := k.LegacyGetAllThrottledPacketData(ctx, consumer.ChainId) + for _, consumerChainID := range k.GetAllRegisteredConsumerChainIDs(ctx) { + slashData, vscmData := k.LegacyGetAllThrottledPacketData(ctx, consumerChainID) if len(slashData) > 0 { k.Logger(ctx).Error(fmt.Sprintf("slash data being dropped: %v", slashData)) } for _, data := range vscmData { - k.HandleVSCMaturedPacket(ctx, consumer.ChainId, data) + k.HandleVSCMaturedPacket(ctx, consumerChainID, data) } - k.LegacyDeleteThrottledPacketDataForConsumer(ctx, consumer.ChainId) + k.LegacyDeleteThrottledPacketDataForConsumer(ctx, consumerChainID) } return nil } diff --git a/x/ccv/provider/migrations/v5/migrations.go b/x/ccv/provider/migrations/v5/migrations.go index 748b35dd0e..7ed8bd8efc 100644 --- a/x/ccv/provider/migrations/v5/migrations.go +++ b/x/ccv/provider/migrations/v5/migrations.go @@ -10,12 +10,9 @@ import ( // If a chain is in voting while the upgrade happens, this is not sufficient, // and a migration to rewrite the proposal is needed. func MigrateTopNForRegisteredChains(ctx sdk.Context, providerKeeper providerkeeper.Keeper) { - // get all consumer chains - registeredConsumerChains := providerKeeper.GetAllConsumerChains(ctx) - // Set the topN of each chain to 95 - for _, chain := range registeredConsumerChains { - providerKeeper.SetTopN(ctx, chain.ChainId, 95) + for _, chainID := range providerKeeper.GetAllRegisteredConsumerChainIDs(ctx) { + providerKeeper.SetTopN(ctx, chainID, 95) } } From 5869098aa9016a6e136b451aaba6bf94d47011ac Mon Sep 17 00:00:00 2001 From: insumity Date: Mon, 10 Jun 2024 15:45:44 +0200 Subject: [PATCH 056/102] feat!: added E2E test and docs for ConsumerModificationProposal (#1949) * added E2E test for the ConsumerModificationProposal * added docs * add to nightly tests * fix markdown links * Update docs/docs/features/proposals.md Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> --------- Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> --- .github/workflows/nightly-e2e.yml | 17 + app/provider/app.go | 1 + docs/docs/features/partial-set-security.md | 5 + docs/docs/features/power-shaping.md | 6 +- docs/docs/features/proposals.md | 34 +- docs/docs/frequently-asked-questions.md | 6 + docs/docs/validators/joining-testnet.md | 4 +- docs/docs/validators/overview.md | 8 +- tests/e2e/actions.go | 73 +++ tests/e2e/main.go | 7 + tests/e2e/state.go | 24 + tests/e2e/steps_partial_set_security.go | 544 +++++++++++++++++++++ tests/e2e/test_driver.go | 2 + tests/integration/distribution.go | 1 + x/ccv/provider/types/proposal.go | 2 +- 15 files changed, 725 insertions(+), 9 deletions(-) diff --git a/.github/workflows/nightly-e2e.yml b/.github/workflows/nightly-e2e.yml index 906b2a2806..7f200b7f97 100644 --- a/.github/workflows/nightly-e2e.yml +++ b/.github/workflows/nightly-e2e.yml @@ -277,6 +277,22 @@ jobs: go-version: "1.21" # The Go version to download (if necessary) and use. - name: E2E partial set security denylist run: go run ./tests/e2e/... --tc partial-set-security-validators-denylisted + partial-set-security-modification-proposal: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - uses: actions/checkout@v4 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.21" # The Go version to download (if necessary) and use. + - name: E2E partial set security modification proposal + run: go run ./tests/e2e/... --tc partial-set-security-modification-proposal nightly-test-fail: needs: @@ -295,6 +311,7 @@ jobs: - partial-set-security-validators-power-cap-test - partial-set-security-validators-allowlisted-test - partial-set-security-validators-denylisted-test + - partial-set-security-modification-proposal if: ${{ failure() }} runs-on: ubuntu-latest steps: diff --git a/app/provider/app.go b/app/provider/app.go index 9cf45fe7e3..47eee5c79c 100644 --- a/app/provider/app.go +++ b/app/provider/app.go @@ -141,6 +141,7 @@ var ( ibcclientclient.UpgradeProposalHandler, ibcproviderclient.ConsumerAdditionProposalHandler, ibcproviderclient.ConsumerRemovalProposalHandler, + ibcproviderclient.ConsumerModificationProposalHandler, ibcproviderclient.ChangeRewardDenomsProposalHandler, }, ), diff --git a/docs/docs/features/partial-set-security.md b/docs/docs/features/partial-set-security.md index a2edbbb15d..6405fe092b 100644 --- a/docs/docs/features/partial-set-security.md +++ b/docs/docs/features/partial-set-security.md @@ -26,3 +26,8 @@ For Top N chains, this is also the long term vision for how they are launched. For Opt In chains, this is a temporary measure to prevent issues around chain ID squatting, i.e. someone could spuriously register many desirable chain IDs of upcoming consumer chain and simply deny legitimate consumer chains from using them. Eventually, the plan is to allow launching Opt In chains permissionlessly without going through governance, with quality control being handled by the market of validators deciding which chains they would like to validate on. ::: + +:::tip +A running Top N consumer chain might want to become an Opt-In chain or vice versa. This can be achieved by issuing +a [`ConsumerModificationProposal`](./proposals.md#consumermodificationproposal). +::: \ No newline at end of file diff --git a/docs/docs/features/power-shaping.md b/docs/docs/features/power-shaping.md index 726ff031c6..2f51aef8f0 100644 --- a/docs/docs/features/power-shaping.md +++ b/docs/docs/features/power-shaping.md @@ -59,4 +59,8 @@ an allowlist that is too short can very quickly become outdated and leave too fe the power distribution on the provider shifts and the denylisted validators gain more power. In general, when setting these parameters, consider that the voting power distribution in the future might be very different from the one right now, -and that the chain should be secure even if the power distribution changes significantly. \ No newline at end of file +and that the chain should be secure even if the power distribution changes significantly. + +:::tip +The power shaping parameters of a running consumer chain can be changed through a [`ConsumerModificationProposal`](./proposals.md#consumermodificationproposal). +::: \ No newline at end of file diff --git a/docs/docs/features/proposals.md b/docs/docs/features/proposals.md index 1f188b8567..5f5457fc0a 100644 --- a/docs/docs/features/proposals.md +++ b/docs/docs/features/proposals.md @@ -5,7 +5,7 @@ sidebar_position: 3 # ICS Provider Proposals -Interchain security module introduces 3 new proposal types to the provider. +Interchain security module introduces new proposal types to the provider. The proposals are used to propose upcoming interchain security events through governance. @@ -84,6 +84,38 @@ After the introduction of Partial Set Security, the use of the soft opt-out mech encouraged to use the topN parameter to not force validators with little stake to validate the chain. ::: + +## `ConsumerModificationProposal` +Proposal type used to change the power shaping parameters of a running consumer chain, as well as to change a Top N running +consumer chain to an Opt-In chain and vice versa. + +When a `ConsumerModificationProposal` passes for a running consumer chain, the consumer chain would change all its +parameters to the ones passed in the `ConsumerModificationProposal`. + +Assume, a `chain-1` is a Top N chain. If the following `ConsumerModificationProposal` passes, then `chain-1` would become +an Opt-In chain with a 40% validators power cap, a maximum number of 30 validators, and one denylisted validator. +```js +{ + "title": "Modify consumer chain", + "description": ".md description of your chain and all other relevant information", + "chain_id": "chain-1", + "top_N": 0, + "validators_power_cap": 40, + "validator_set_cap": 30, + "allowlist": [], + "denylist": ["cosmosvalcons1qmq08eruchr5sf5s3rwz7djpr5a25f7xw4mceq"] +} +``` + +:::warning +If `top_N`, `validators_power_cap`, or some other argument is not included in the proposal, then it is considered +that the default value is set for this argument. For example, if a Top 50% chain wants to only modify `validators_power_cap` +from 35 to 40, then the `ConsumerModificationProposal` would still need to include that `top_N` is 50. Otherwise +`top_N` would be set to its default value of 0, and the chain would become an Opt-In chain. + +To be **safe**, always include `top_N` and all the power shaping parameters in your `ConsumerModificationProposal`. +::: + ## ChangeRewardDenomProposal Proposal type used to mutate the set of denoms accepted by the provider as rewards. diff --git a/docs/docs/frequently-asked-questions.md b/docs/docs/frequently-asked-questions.md index f648f8eac6..c78dc89f0f 100644 --- a/docs/docs/frequently-asked-questions.md +++ b/docs/docs/frequently-asked-questions.md @@ -129,3 +129,9 @@ Yes, the consumer chain will halt with an ERR CONSENSUS FAILURE error after the ## Can validators set a commission rate for chains they have not opted in to? Yes, and this is useful for validators that are not in the top N% of the provider chain, but might move into the top N% in the future. By setting the commission rate ahead of time, they can make sure that they immediately have a commission rate of their choosing as soon as they are in the top N%. + +## Can a consumer chain modify its power shaping parameters? +Yes, by issuing a [`ConsumerModificationProposal`](./features/proposals.md#consumermodificationproposal). + +## Can a Top N consumer chain become Opt-In or vice versa? +Yes, by issuing a [`ConsumerModificationProposal`](./features/proposals.md#consumermodificationproposal). \ No newline at end of file diff --git a/docs/docs/validators/joining-testnet.md b/docs/docs/validators/joining-testnet.md index 86a3dc2693..d61be07e12 100644 --- a/docs/docs/validators/joining-testnet.md +++ b/docs/docs/validators/joining-testnet.md @@ -12,7 +12,7 @@ The experience gained in the testnet will prepare you for validating interchain :::tip Provider and consumer chain represent distinct networks and infrastructures operated by the same validator set. -For general information about running cosmos-sdk based chains check out the [validator basics](https://hub.cosmos.network/validators/validator-setup) and [Running a Node section](https://docs.cosmos.network/main/run-node/run-node) of Cosmos SDK docs +For general information about running cosmos-sdk based chains check out the [validator basics](https://hub.cosmos.network/main/validators/validator-setup) and [Running a Node section](https://docs.cosmos.network/main/run-node/run-node) of Cosmos SDK docs ::: ## Joining the provider chain @@ -79,7 +79,7 @@ gaiad tx staking create-validator \ ``` :::tip -Check this [guide](https://hub.cosmos.network/validators/validator-setup#edit-validator-description) to edit your validator. +Check this [guide](https://hub.cosmos.network/main/validators/validator-setup#edit-validator-description) to edit your validator. ::: After this step, your validator is created and you can start your node and catch up to the rest of the network. It is recommended that you use `statesync` to catch up to the rest of the network. diff --git a/docs/docs/validators/overview.md b/docs/docs/validators/overview.md index 97ef906d0a..ff22edb560 100644 --- a/docs/docs/validators/overview.md +++ b/docs/docs/validators/overview.md @@ -85,7 +85,7 @@ At present, the consumer chain can report evidence about downtime infractions to :::info Causing a downtime infraction on any consumer chain will not incur a slash penalty. Instead, the offending validator will be jailed on the provider chain and consequently on all consumer chains. -To unjail, the validator must wait for the jailing period to elapse on the provider chain and [submit an unjail transaction](https://hub.cosmos.network/validators/validator-setup#unjail-validator) on the provider chain. After unjailing on the provider, the validator will be unjailed on all consumer chains. +To unjail, the validator must wait for the jailing period to elapse on the provider chain and [submit an unjail transaction](https://hub.cosmos.network/main/validators/validator-setup#unjail-validator) on the provider chain. After unjailing on the provider, the validator will be unjailed on all consumer chains. More information is available in [Downtime Slashing documentation](../features/slashing.md#downtime-infractions) ::: @@ -99,7 +99,7 @@ Validators can use different consensus keys on the provider and each of the cons For more information check out the [Key assignment overview and guide](../features/key-assignment.md) ## References: -- [Cosmos Hub Validators FAQ](https://hub.cosmos.network/validators/validator-faq) -- [Cosmos Hub Running a validator](https://hub.cosmos.network/validators/validator-setup) +- [Cosmos Hub Validators FAQ](https://hub.cosmos.network/main/validators/validator-faq) +- [Cosmos Hub Running a validator](https://hub.cosmos.network/main/validators/validator-setup) - [Startup Sequence](https://github.com/cosmos/testnets/blob/master/interchain-security/CONSUMER_LAUNCH_GUIDE.md#chain-launch) -- [Submit Unjailing Transaction](https://hub.cosmos.network/validators/validator-setup#unjail-validator) +- [Submit Unjailing Transaction](https://hub.cosmos.network/main/validators/validator-setup#unjail-validator) diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 32e8604709..2e8b500eb7 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -399,6 +399,79 @@ func (tr TestConfig) submitConsumerRemovalProposal( tr.waitBlocks(ChainID("provi"), 2, 20*time.Second) } +type SubmitConsumerModificationProposalAction struct { + Chain ChainID + From ValidatorID + Deposit uint + ConsumerChain ChainID + TopN uint32 + ValidatorsPowerCap uint32 + ValidatorSetCap uint32 + Allowlist []string + Denylist []string +} + +func (tr TestConfig) submitConsumerModificationProposal( + action SubmitConsumerModificationProposalAction, + target ExecutionTarget, + verbose bool, +) { + prop := client.ConsumerModificationProposalJSON{ + Title: "Propose the modification of the PSS parameters of a chain", + Summary: "summary of a modification proposal", + ChainId: string(tr.chainConfigs[action.ConsumerChain].ChainId), + Deposit: fmt.Sprint(action.Deposit) + `stake`, + TopN: action.TopN, + ValidatorsPowerCap: action.ValidatorsPowerCap, + ValidatorSetCap: action.ValidatorSetCap, + Allowlist: action.Allowlist, + Denylist: action.Denylist, + } + + bz, err := json.Marshal(prop) + if err != nil { + log.Fatal(err) + } + + jsonStr := string(bz) + if strings.Contains(jsonStr, "'") { + log.Fatal("prop json contains single quote") + } + + //#nosec G204 -- bypass unsafe quoting warning (no production code) + bz, err = target.ExecCommand( + "/bin/bash", "-c", fmt.Sprintf(`echo '%s' > %s`, jsonStr, "/temp-proposal.json"), + ).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + // CONSUMER MODIFICATION PROPOSAL + cmd := target.ExecCommand( + tr.chainConfigs[action.Chain].BinaryName, + "tx", "gov", "submit-legacy-proposal", "consumer-modification", "/temp-proposal.json", + `--from`, `validator`+fmt.Sprint(action.From), + `--chain-id`, string(tr.chainConfigs[action.Chain].ChainId), + `--home`, tr.getValidatorHome(action.Chain, action.From), + `--gas`, `900000`, + `--node`, tr.getValidatorNode(action.Chain, action.From), + `--keyring-backend`, `test`, + `-y`, + ) + if verbose { + log.Println("submitConsumerModificationProposal cmd: ", cmd.String()) + } + + bz, err = cmd.CombinedOutput() + + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + // wait for inclusion in a block -> '--broadcast-mode block' is deprecated + tr.waitBlocks(ChainID("provi"), 2, 10*time.Second) +} + type SubmitParamChangeLegacyProposalAction struct { Chain ChainID From ValidatorID diff --git a/tests/e2e/main.go b/tests/e2e/main.go index 9fb57f286a..6a7523ac05 100644 --- a/tests/e2e/main.go +++ b/tests/e2e/main.go @@ -192,6 +192,12 @@ var stepChoices = map[string]StepChoice{ description: "test partial set security for an Opt-In chain that has a validator denylisted", testConfig: DefaultTestCfg, }, + "partial-set-security-modification-proposal": { + name: "partial-set-security-modification-proposal", + steps: stepsModifyChain(), + description: "test partial set security parameters can be changed through a modification proposal", + testConfig: DefaultTestCfg, + }, } func getTestCaseUsageString() string { @@ -280,6 +286,7 @@ func getTestCases(selectedPredefinedTests, selectedTestFiles TestSet, providerVe "consumer-double-downtime", "partial-set-security-opt-in", "partial-set-security-top-n", "partial-set-security-validator-set-cap", "partial-set-security-validators-power-cap", "partial-set-security-validators-allowlisted", "partial-set-security-validators-denylisted", + "partial-set-security-modification-proposal", } if includeMultiConsumer != nil && *includeMultiConsumer { selectedPredefinedTests = append(selectedPredefinedTests, "multiconsumer") diff --git a/tests/e2e/state.go b/tests/e2e/state.go index 7b674b0df8..636121333f 100644 --- a/tests/e2e/state.go +++ b/tests/e2e/state.go @@ -76,6 +76,14 @@ type ConsumerRemovalProposal struct { func (p ConsumerRemovalProposal) isProposal() {} +type ConsumerModificationProposal struct { + Deposit uint + Chain ChainID + Status string +} + +func (p ConsumerModificationProposal) isProposal() {} + type Rewards struct { IsRewarded map[ValidatorID]bool // if true it will calculate if the validator/delegator is rewarded between 2 successive blocks, @@ -482,6 +490,22 @@ func (tr TestConfig) getProposal(chain ChainID, proposal uint) Proposal { Chain: chain, StopTime: int(stopTime.Milliseconds()), } + case "/interchain_security.ccv.provider.v1.ConsumerModificationProposal": + chainId := gjson.Get(string(bz), `messages.0.content.chain_id`).String() + + var chain ChainID + for i, conf := range tr.chainConfigs { + if string(conf.ChainId) == chainId { + chain = i + break + } + } + + return ConsumerModificationProposal{ + Deposit: uint(deposit), + Status: status, + Chain: chain, + } case "/cosmos.params.v1beta1.ParameterChangeProposal": return ParamsProposal{ Deposit: uint(deposit), diff --git a/tests/e2e/steps_partial_set_security.go b/tests/e2e/steps_partial_set_security.go index 5e86ede3a8..1002302620 100644 --- a/tests/e2e/steps_partial_set_security.go +++ b/tests/e2e/steps_partial_set_security.go @@ -1892,3 +1892,547 @@ func stepsValidatorsDenylistedChain() []Step { return s } + +// stepsModifyChain issues multiple `ConsumerModificationProposal`s on a consumer chain to assert that indeed +// partial-set security parameters can be changed. +func stepsModifyChain() []Step { + s := []Step{ + { + Action: StartChainAction{ + Chain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + }, + }, + { + Action: SubmitConsumerAdditionProposalAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + Deposit: 10000001, + ConsumerChain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + TopN: 0, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerAdditionProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_VOTING_PERIOD", + }, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + // Οpt in "alice", "bob", and "carol." Note, that "alice" and "bob" use the provider's public key + // (see `UseConsumerKey` is set to `false` in `getDefaultValidators`) and hence do not need a consumer-key assignment. + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("alice"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, // chain is not running yet + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("bob"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + }, + State: State{ + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + // assign the consumer key "carol" is using on the consumer chain to be the one "carol" uses when opting in + Action: AssignConsumerPubKeyAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + // reconfigure the node -> validator was using provider key + // until this point -> key matches config.consumerValPubKey for "carol" + ConsumerPubkey: getDefaultValidators()[ValidatorID("carol")].ConsumerValPubKey, + ReconfigureNode: true, + }, + State: State{}, + }, + { + Action: VoteGovProposalAction{ + Chain: ChainID("provi"), + From: []ValidatorID{ValidatorID("alice"), ValidatorID("bob"), ValidatorID("carol")}, + Vote: []string{"yes", "yes", "yes"}, + PropNumber: 1, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerAdditionProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_PASSED", + }, + }, + }, + }, + }, + { + Action: StartConsumerChainAction{ + ConsumerChain: ChainID("consu"), + ProviderChain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + // For consumers that're launching with the provider being on an earlier version + // of ICS before the soft opt-out threshold was introduced, we need to set the + // soft opt-out threshold to 0.05 in the consumer genesis to ensure that the + // consumer binary doesn't panic. Sdk requires that all params are set to valid + // values from the genesis file. + GenesisChanges: ".app_state.ccvconsumer.params.soft_opt_out_threshold = \"0.05\"", + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + }, + }, + { + Action: AddIbcConnectionAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ClientA: 0, + ClientB: 0, + }, + State: State{}, + }, + { + Action: AddIbcChannelAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ConnectionA: 0, + PortA: "consumer", + PortB: "provider", + Order: "ordered", + }, + State: State{}, + }, + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + ChainID("provi"): ChainState{ + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + + // In what follows, we have 5 cases of `ConsumerModificationProposal`s that test the following: + // 1. set `ValidatorsPowerCap` to 40% + // 2. set the `ValidatorSetCap` to a maximum of 2 validators + // 3. set an allowlist with 2 validators + // 4. set a denylist with 1 validator + // 5. modify the chain from Opt In to Top 100% + + // 1. set `ValidatorsPowerCap` to 40% + { + Action: SubmitConsumerModificationProposalAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + Deposit: 10000001, + ConsumerChain: ChainID("consu"), + ValidatorsPowerCap: 40, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 2: ConsumerModificationProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + Status: "PROPOSAL_STATUS_VOTING_PERIOD", + }, + }, + }, + }, + }, + { + Action: VoteGovProposalAction{ + Chain: ChainID("provi"), + From: []ValidatorID{ValidatorID("alice"), ValidatorID("bob"), ValidatorID("carol")}, + Vote: []string{"yes", "yes", "yes"}, + PropNumber: 2, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 2: ConsumerModificationProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + Status: "PROPOSAL_STATUS_PASSED", + }, + }, + }, + }, + }, + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + ChainID("consu"): ChainState{ + // `ValidatorsPowerCap` is set to 40% + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 130, // ~22% of the total voting power + ValidatorID("bob"): 230, // ~38% of the total voting power + ValidatorID("carol"): 240, // 40% of the total voting power + }, + }, + }, + }, + + // 2. set the `ValidatorSetCap` to a maximum of 2 validators + { + Action: SubmitConsumerModificationProposalAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + Deposit: 10000001, + ConsumerChain: ChainID("consu"), + ValidatorSetCap: 2, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 3: ConsumerModificationProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + Status: "PROPOSAL_STATUS_VOTING_PERIOD", + }, + }, + }, + }, + }, + { + Action: VoteGovProposalAction{ + Chain: ChainID("provi"), + From: []ValidatorID{ValidatorID("alice"), ValidatorID("bob"), ValidatorID("carol")}, + Vote: []string{"yes", "yes", "yes"}, + PropNumber: 3, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 3: ConsumerModificationProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + Status: "PROPOSAL_STATUS_PASSED", + }, + }, + }, + }, + }, + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + ChainID("consu"): ChainState{ + // we can have a maximum of 2 validators due to `ValidatorSetCap`, hence only the 2 validators ("bob" and "carol") + // with the highest voting power validate + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + }, + }, + + // 3. set an allowlist with 2 validators + { + Action: SubmitConsumerModificationProposalAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + Deposit: 10000001, + ConsumerChain: ChainID("consu"), + // only "alice" and "carol" are allowlisted (see `getDefaultValidators` in `tests/e2e/config.go`) + Allowlist: []string{"cosmosvalcons1qmq08eruchr5sf5s3rwz7djpr5a25f7xw4mceq", + "cosmosvalcons1ezyrq65s3gshhx5585w6mpusq3xsj3ayzf4uv6"}, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 4: ConsumerModificationProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + Status: "PROPOSAL_STATUS_VOTING_PERIOD", + }, + }, + }, + }, + }, + { + Action: VoteGovProposalAction{ + Chain: ChainID("provi"), + From: []ValidatorID{ValidatorID("alice"), ValidatorID("bob"), ValidatorID("carol")}, + Vote: []string{"yes", "yes", "yes"}, + PropNumber: 4, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 4: ConsumerModificationProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + Status: "PROPOSAL_STATUS_PASSED", + }, + }, + }, + }, + }, + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 0, // "bob" is not allowlisted + ValidatorID("carol"): 300, + }, + }, + }, + }, + + // 4. set a denylist with 1 validator + { + Action: SubmitConsumerModificationProposalAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + Deposit: 10000001, + ConsumerChain: ChainID("consu"), + // only "alice" is denylisted (see `getDefaultValidators` in `tests/e2e/config.go`) + Denylist: []string{"cosmosvalcons1qmq08eruchr5sf5s3rwz7djpr5a25f7xw4mceq"}, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 5: ConsumerModificationProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + Status: "PROPOSAL_STATUS_VOTING_PERIOD", + }, + }, + }, + }, + }, + { + Action: VoteGovProposalAction{ + Chain: ChainID("provi"), + From: []ValidatorID{ValidatorID("alice"), ValidatorID("bob"), ValidatorID("carol")}, + Vote: []string{"yes", "yes", "yes"}, + PropNumber: 5, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 5: ConsumerModificationProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + Status: "PROPOSAL_STATUS_PASSED", + }, + }, + }, + }, + }, + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, // "alice" is denylisted + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + }, + }, + }, + + // 5. modify the chain from Opt In to Top 100% + { + Action: SubmitConsumerModificationProposalAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + Deposit: 10000001, + ConsumerChain: ChainID("consu"), + TopN: 100, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 6: ConsumerModificationProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + Status: "PROPOSAL_STATUS_VOTING_PERIOD", + }, + }, + }, + }, + }, + { + Action: VoteGovProposalAction{ + Chain: ChainID("provi"), + From: []ValidatorID{ValidatorID("alice"), ValidatorID("bob"), ValidatorID("carol")}, + Vote: []string{"yes", "yes", "yes"}, + PropNumber: 6, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 6: ConsumerModificationProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + Status: "PROPOSAL_STATUS_PASSED", + }, + }, + }, + }, + }, + { + Action: OptOutAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("alice"), + ExpectError: true, // because this chain is now Top 100%, no validator can opt out + }, + State: State{}, + }, + { + Action: OptOutAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("bob"), + ExpectError: true, // because this chain is now Top 100%, no validator can opt out + }, + State: State{}, + }, + { + Action: OptOutAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("carol"), + ExpectError: true, // because this chain is now Top 100%, no validator can opt out + }, + State: State{}, + }} + + return s +} diff --git a/tests/e2e/test_driver.go b/tests/e2e/test_driver.go index a12dc7862e..245d70020b 100644 --- a/tests/e2e/test_driver.go +++ b/tests/e2e/test_driver.go @@ -80,6 +80,8 @@ func (td *DefaultDriver) runAction(action interface{}) error { td.testCfg.submitConsumerAdditionProposal(action, td.target, td.verbose) case SubmitConsumerRemovalProposalAction: td.testCfg.submitConsumerRemovalProposal(action, td.target, td.verbose) + case SubmitConsumerModificationProposalAction: + td.testCfg.submitConsumerModificationProposal(action, td.target, td.verbose) case SubmitParamChangeLegacyProposalAction: td.testCfg.submitParamChangeProposal(action, td.target, td.verbose) case VoteGovProposalAction: diff --git a/tests/integration/distribution.go b/tests/integration/distribution.go index a71edf7f77..8229850a63 100644 --- a/tests/integration/distribution.go +++ b/tests/integration/distribution.go @@ -59,6 +59,7 @@ func (s *CCVTestSuite) TestRewardsDistribution() { fees := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))) err := consumerBankKeeper.SendCoinsFromAccountToModule(s.consumerCtx(), s.consumerChain.SenderAccount.GetAddress(), authtypes.FeeCollectorName, fees) s.Require().NoError(err) + feePoolTokens := consumerBankKeeper.GetAllBalances(s.consumerCtx(), consumerFeePoolAddr) s.Require().Equal(sdk.NewInt(100).Add(feePoolTokensOld.AmountOf(sdk.DefaultBondDenom)), feePoolTokens.AmountOf(sdk.DefaultBondDenom)) diff --git a/x/ccv/provider/types/proposal.go b/x/ccv/provider/types/proposal.go index 0e42f2a3ab..97b9bd158f 100644 --- a/x/ccv/provider/types/proposal.go +++ b/x/ccv/provider/types/proposal.go @@ -235,7 +235,7 @@ func (sccp *ConsumerRemovalProposal) ValidateBasic() error { return nil } -// NewConsumerModificationProposal creates a new consumer modificaton proposal. +// NewConsumerModificationProposal creates a new consumer modification proposal. func NewConsumerModificationProposal(title, description, chainID string, topN uint32, validatorsPowerCap uint32, From c93f0756c835246b7afe70dc0dd61105f39aa872 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jun 2024 10:09:32 +0200 Subject: [PATCH 057/102] build(deps): bump golang.org/x/mod from 0.17.0 to 0.18.0 (#1955) Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.17.0 to 0.18.0. - [Commits](https://github.com/golang/mod/compare/v0.17.0...v0.18.0) --- updated-dependencies: - dependency-name: golang.org/x/mod dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a725965de2..5b46550220 100644 --- a/go.mod +++ b/go.mod @@ -166,7 +166,7 @@ require ( require ( github.com/informalsystems/itf-go v0.0.1 github.com/spf13/viper v1.19.0 - golang.org/x/mod v0.17.0 + golang.org/x/mod v0.18.0 google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 ) diff --git a/go.sum b/go.sum index e2ef9f9286..6640cdf97a 100644 --- a/go.sum +++ b/go.sum @@ -1098,8 +1098,8 @@ golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.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= From a2b718c634bd1c8ed76996fd61b528c932543ea7 Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Tue, 11 Jun 2024 10:57:56 +0200 Subject: [PATCH 058/102] chore: add last PSS changes to changelog (#1944) add last PSS changes to changelog --- .../features/provider/1932-allow-pss-params-changes.md | 2 ++ .../state-breaking/provider/1925-apply-audit-suggestions.md | 3 +-- .../state-breaking/provider/1932-allow-pss-params-changes.md | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 .changelog/unreleased/features/provider/1932-allow-pss-params-changes.md create mode 100644 .changelog/unreleased/state-breaking/provider/1932-allow-pss-params-changes.md diff --git a/.changelog/unreleased/features/provider/1932-allow-pss-params-changes.md b/.changelog/unreleased/features/provider/1932-allow-pss-params-changes.md new file mode 100644 index 0000000000..0bbbac5ffa --- /dev/null +++ b/.changelog/unreleased/features/provider/1932-allow-pss-params-changes.md @@ -0,0 +1,2 @@ +- Allow consumer chains to change their PSS parameters. + ([\#1932](https://github.com/cosmos/interchain-security/pull/1932)) diff --git a/.changelog/unreleased/state-breaking/provider/1925-apply-audit-suggestions.md b/.changelog/unreleased/state-breaking/provider/1925-apply-audit-suggestions.md index 3d12033a4e..018c0eafb1 100644 --- a/.changelog/unreleased/state-breaking/provider/1925-apply-audit-suggestions.md +++ b/.changelog/unreleased/state-breaking/provider/1925-apply-audit-suggestions.md @@ -1,3 +1,2 @@ - Apply audit suggestions that include a bug fix in the way we compute the - maximum capped power. ([\#1925](https://github.com/cosmos/interchain- - security/pull/1925)) + maximum capped power. ([\#1925](https://github.com/cosmos/interchain-security/pull/1925)) diff --git a/.changelog/unreleased/state-breaking/provider/1932-allow-pss-params-changes.md b/.changelog/unreleased/state-breaking/provider/1932-allow-pss-params-changes.md new file mode 100644 index 0000000000..0bbbac5ffa --- /dev/null +++ b/.changelog/unreleased/state-breaking/provider/1932-allow-pss-params-changes.md @@ -0,0 +1,2 @@ +- Allow consumer chains to change their PSS parameters. + ([\#1932](https://github.com/cosmos/interchain-security/pull/1932)) From 8a8e7a04efd07983dbf0e165aef9b1009f3e88ec Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Tue, 11 Jun 2024 11:14:06 +0200 Subject: [PATCH 059/102] test: Add integration test reproducing the LastValidators exceeding MaxValidators bug (#1945) * Add test reproducing the LastValidators exceeding MaxValidators * formatting * Update tests/integration/unbonding.go Co-authored-by: insumity * Update tests/integration/unbonding.go Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> * document --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: insumity Co-authored-by: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> --- tests/integration/unbonding.go | 49 ++++++++++++++++++++++++++++++ testutil/integration/debug_test.go | 4 +++ testutil/integration/interfaces.go | 4 +++ testutil/keeper/mocks.go | 28 +++++++++++++++++ 4 files changed, 85 insertions(+) diff --git a/tests/integration/unbonding.go b/tests/integration/unbonding.go index 7f87516444..a8398843aa 100644 --- a/tests/integration/unbonding.go +++ b/tests/integration/unbonding.go @@ -461,3 +461,52 @@ func (s *CCVTestSuite) TestRedelegationProviderFirst() { // Check that ccv unbonding op has been deleted checkCCVUnbondingOp(s, s.providerCtx(), s.consumerChain.ChainID, valsetUpdateID, false) } + +// This test reproduces a fixed bug when an inactive validator enters back into the active set. +// It used to cause a panic in the provider module hook called by AfterUnbondingInitiated +// during the staking module EndBlock. +func (s *CCVTestSuite) TestTooManyLastValidators() { + sk := s.providerApp.GetTestStakingKeeper() + + // get current staking params + p := sk.GetParams(s.providerCtx()) + + // get validators, which are all active at the moment + vals := sk.GetAllValidators(s.providerCtx()) + s.Require().Equal(len(vals), len(sk.GetLastValidators(s.providerCtx()))) + + // jail a validator + val := vals[0] + consAddr, err := val.GetConsAddr() + s.Require().NoError(err) + sk.Jail(s.providerCtx(), consAddr) + + // save the current number of bonded vals + lastVals := sk.GetLastValidators(s.providerCtx()) + + // pass one block to apply the validator set changes + // (calls ApplyAndReturnValidatorSetUpdates in the the staking module EndBlock) + s.providerChain.NextBlock() + + // verify that the number of bonded validators is decreased by one + s.Require().Equal(len(lastVals)-1, len(sk.GetLastValidators(s.providerCtx()))) + + // update maximum validator to equal the number of bonded validators + p.MaxValidators = uint32(len(sk.GetLastValidators(s.providerCtx()))) + sk.SetParams(s.providerCtx(), p) + + // pass one block to apply validator set changes + s.providerChain.NextBlock() + + // unjail validator + // Note that since validators are sorted in descending order, the unjailed validator + // enters the active set again since it's ranked first by voting power. + sk.Unjail(s.providerCtx(), consAddr) + + // pass another block to update the validator set + // which causes a panic due to a GetLastValidator call in + // ApplyAndReturnValidatorSetUpdates where the staking module has a inconsistent state + s.Require().NotPanics(s.providerChain.NextBlock) + s.Require().NotPanics(func() { sk.ApplyAndReturnValidatorSetUpdates(s.providerCtx()) }) + s.Require().NotPanics(func() { sk.GetLastValidators(s.providerCtx()) }) +} diff --git a/testutil/integration/debug_test.go b/testutil/integration/debug_test.go index 2481e865ab..d97c41f7d0 100644 --- a/testutil/integration/debug_test.go +++ b/testutil/integration/debug_test.go @@ -303,3 +303,7 @@ func TestAllocateTokensToValidator(t *testing.T) { func TestMultiConsumerRewardsDistribution(t *testing.T) { runCCVTestByName(t, "TestMultiConsumerRewardsDistribution") } + +func TestTooManyLastValidators(t *testing.T) { + runCCVTestByName(t, "TestTooManyLastValidators") +} diff --git a/testutil/integration/interfaces.go b/testutil/integration/interfaces.go index fe3382b524..5324449c9f 100644 --- a/testutil/integration/interfaces.go +++ b/testutil/integration/interfaces.go @@ -3,6 +3,7 @@ package integration import ( "time" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ibctesting "github.com/cosmos/ibc-go/v7/testing" "cosmossdk.io/math" @@ -106,6 +107,9 @@ type TestStakingKeeper interface { ) (ubd types.UnbondingDelegation, found bool) GetAllValidators(ctx sdk.Context) (validators []types.Validator) GetValidatorSet() types.ValidatorSet + GetParams(ctx sdk.Context) stakingtypes.Params + SetParams(ctx sdk.Context, p stakingtypes.Params) error + ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []abci.ValidatorUpdate, err error) } type TestBankKeeper interface { diff --git a/testutil/keeper/mocks.go b/testutil/keeper/mocks.go index ffa76ad40c..0ad09a0be6 100644 --- a/testutil/keeper/mocks.go +++ b/testutil/keeper/mocks.go @@ -119,6 +119,20 @@ func (mr *MockStakingKeeperMockRecorder) GetLastValidators(ctx interface{}) *gom return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastValidators", reflect.TypeOf((*MockStakingKeeper)(nil).GetLastValidators), ctx) } +// GetParams mocks base method. +func (m *MockStakingKeeper) GetParams(ctx types0.Context) types5.Params { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetParams", ctx) + ret0, _ := ret[0].(types5.Params) + return ret0 +} + +// GetParams indicates an expected call of GetParams. +func (mr *MockStakingKeeperMockRecorder) GetParams(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetParams", reflect.TypeOf((*MockStakingKeeper)(nil).GetParams), ctx) +} + // GetRedelegationByUnbondingID mocks base method. func (m *MockStakingKeeper) GetRedelegationByUnbondingID(ctx types0.Context, id uint64) (types5.Redelegation, bool) { m.ctrl.T.Helper() @@ -357,6 +371,20 @@ func (mr *MockStakingKeeperMockRecorder) PutUnbondingOnHold(ctx, id interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutUnbondingOnHold", reflect.TypeOf((*MockStakingKeeper)(nil).PutUnbondingOnHold), ctx, id) } +// SetParams mocks base method. +func (m *MockStakingKeeper) SetParams(ctx types0.Context, p types5.Params) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetParams", ctx, p) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetParams indicates an expected call of SetParams. +func (mr *MockStakingKeeperMockRecorder) SetParams(ctx, p interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetParams", reflect.TypeOf((*MockStakingKeeper)(nil).SetParams), ctx, p) +} + // Slash mocks base method. func (m *MockStakingKeeper) Slash(arg0 types0.Context, arg1 types0.ConsAddress, arg2, arg3 int64, arg4 types0.Dec) math.Int { m.ctrl.T.Helper() From 198a26fbd72901207b577ca306bca5ae5b6b0b15 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Tue, 11 Jun 2024 14:07:38 +0200 Subject: [PATCH 060/102] test: Add an e2e test that reproduces the chain halt (#1942) * Start writing e2e test with unjailing * Add e2e steps for too many validators bug * Fix test config and setup * Change test to use top N chain * Add comment for panic * Start cleaning up active/inactive vals e2e test * Revert change to StartChains * Revert changes to partial-set-security tests * Rename test case * Rename CLI flag for test case * Address comments * Add active set changes test to nightly runs * Fix merge in main.go --- .github/workflows/nightly-e2e.yml | 20 ++++ tests/e2e/config.go | 19 ++++ tests/e2e/main.go | 7 ++ tests/e2e/steps_active_set_changes.go | 138 ++++++++++++++++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 tests/e2e/steps_active_set_changes.go diff --git a/.github/workflows/nightly-e2e.yml b/.github/workflows/nightly-e2e.yml index 7f200b7f97..e872f10230 100644 --- a/.github/workflows/nightly-e2e.yml +++ b/.github/workflows/nightly-e2e.yml @@ -293,6 +293,22 @@ jobs: go-version: "1.21" # The Go version to download (if necessary) and use. - name: E2E partial set security modification proposal run: go run ./tests/e2e/... --tc partial-set-security-modification-proposal + active-set-changes-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - uses: actions/checkout@v4 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.21" # The Go version to download (if necessary) and use. + - name: E2E active set changes + run: go run ./tests/e2e/... --tc active-set-changes nightly-test-fail: needs: @@ -311,7 +327,11 @@ jobs: - partial-set-security-validators-power-cap-test - partial-set-security-validators-allowlisted-test - partial-set-security-validators-denylisted-test +<<<<<<< HEAD + - active-set-changes-test +======= - partial-set-security-modification-proposal +>>>>>>> main if: ${{ failure() }} runs-on: ubuntu-latest steps: diff --git a/tests/e2e/config.go b/tests/e2e/config.go index a5b86e85f9..c172c8584d 100644 --- a/tests/e2e/config.go +++ b/tests/e2e/config.go @@ -87,6 +87,7 @@ const ( MulticonsumerTestCfg TestConfigType = "multi-consumer" ConsumerMisbehaviourTestCfg TestConfigType = "consumer-misbehaviour" CompatibilityTestCfg TestConfigType = "compatibility" + SmallMaxValidatorsTestCfg TestConfigType = "small-max-validators" ) // Attributes that are unique to a validator. Allows us to map (part of) @@ -264,6 +265,8 @@ func GetTestConfig(cfgType TestConfigType, providerVersion, consumerVersion stri testCfg = ConsumerMisbehaviourTestConfig() case CompatibilityTestCfg: testCfg = CompatibilityTestConfig(pv, cv) + case SmallMaxValidatorsTestCfg: + testCfg = SmallMaxValidatorsTestConfig() default: panic(fmt.Sprintf("Invalid test config: %s", cfgType)) } @@ -605,6 +608,22 @@ func DemocracyTestConfig(allowReward bool) TestConfig { return tr } +func SmallMaxValidatorsTestConfig() TestConfig { + cfg := DefaultTestConfig() + + // set the MaxValidators to 2 + proviConfig := cfg.chainConfigs[ChainID("provi")] + proviConfig.GenesisChanges += "| .app_state.staking.params.max_validators = 2" + cfg.chainConfigs[ChainID("provi")] = proviConfig + + carolConfig := cfg.validatorConfigs["carol"] + // make carol use her own key + carolConfig.UseConsumerKey = false + cfg.validatorConfigs["carol"] = carolConfig + + return cfg +} + func MultiConsumerTestConfig() TestConfig { tr := TestConfig{ name: string(MulticonsumerTestCfg), diff --git a/tests/e2e/main.go b/tests/e2e/main.go index 6a7523ac05..5a1ee681e0 100644 --- a/tests/e2e/main.go +++ b/tests/e2e/main.go @@ -192,6 +192,12 @@ var stepChoices = map[string]StepChoice{ description: "test partial set security for an Opt-In chain that has a validator denylisted", testConfig: DefaultTestCfg, }, + "active-set-changes": { + name: "active-set-changes", + steps: stepsActiveSetChanges(), + description: "This is a regression test related to the issue discussed here: https://forum.cosmos.network/t/cosmos-hub-v17-1-chain-halt-post-mortem/13899. The test ensures that the protocol works as expected when MaxValidators is smaller than the number of potential validators.", + testConfig: SmallMaxValidatorsTestCfg, + }, "partial-set-security-modification-proposal": { name: "partial-set-security-modification-proposal", steps: stepsModifyChain(), @@ -286,6 +292,7 @@ func getTestCases(selectedPredefinedTests, selectedTestFiles TestSet, providerVe "consumer-double-downtime", "partial-set-security-opt-in", "partial-set-security-top-n", "partial-set-security-validator-set-cap", "partial-set-security-validators-power-cap", "partial-set-security-validators-allowlisted", "partial-set-security-validators-denylisted", + "active-set-changes", "partial-set-security-modification-proposal", } if includeMultiConsumer != nil && *includeMultiConsumer { diff --git a/tests/e2e/steps_active_set_changes.go b/tests/e2e/steps_active_set_changes.go new file mode 100644 index 0000000000..153e7a07b4 --- /dev/null +++ b/tests/e2e/steps_active_set_changes.go @@ -0,0 +1,138 @@ +package main + +import clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + +// stepsActiveSetChanges starts a top N provider chain and causes a change in the active set +func stepsActiveSetChanges() []Step { + s := []Step{ + // === setup provider chain, consumer chain with top N = 100, and start IBC connections === + { + Action: StartChainAction{ + Chain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 700000000, Allocation: 10000000000}, + }, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, // MaxValidators is set to 2, so alice is not part of the validator set + ValidatorID("bob"): 200, + ValidatorID("carol"): 700, + }, + }, + }, + }, + { + Action: SubmitConsumerAdditionProposalAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + Deposit: 10000001, + ConsumerChain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + TopN: 100, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerAdditionProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_VOTING_PERIOD", + }, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + { + Action: VoteGovProposalAction{ + Chain: ChainID("provi"), + From: []ValidatorID{ValidatorID("alice"), ValidatorID("bob"), ValidatorID("carol")}, + Vote: []string{"yes", "yes", "yes"}, + PropNumber: 1, + }, + State: State{ + ChainID("provi"): ChainState{ + Proposals: &map[uint]Proposal{ + 1: ConsumerAdditionProposal{ + Deposit: 10000001, + Chain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + Status: "PROPOSAL_STATUS_PASSED", + }, + }, + }, + }, + }, + { + // we start all the validators but only "alice" and "bob" have opted in and hence + // only "alice" and "bob" are validating blocks + Action: StartConsumerChainAction{ + ConsumerChain: ChainID("consu"), + ProviderChain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 100000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 200000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 700000000, Allocation: 10000000000}, + }, + // For consumers that're launching with the provider being on an earlier version + // of ICS before the soft opt-out threshold was introduced, we need to set the + // soft opt-out threshold to 0.05 in the consumer genesis to ensure that the + // consumer binary doesn't panic. Sdk requires that all params are set to valid + // values from the genesis file. + GenesisChanges: ".app_state.ccvconsumer.params.soft_opt_out_threshold = \"0.05\"", + }, + State: State{}, + }, + { + Action: AddIbcConnectionAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ClientA: 0, + ClientB: 0, + }, + State: State{}, + }, + { + Action: AddIbcChannelAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + ConnectionA: 0, + PortA: "consumer", + PortB: "provider", + Order: "ordered", + }, + State: State{}, + }, + // === setup ends, ready for the actual test === + // bob incurs downtime on the provider and gets jailed + { + Action: DowntimeSlashAction{ + Chain: ChainID("provi"), + Validator: ValidatorID("bob"), + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, // alice goes into the active set + ValidatorID("bob"): 0, + ValidatorID("carol"): 700, + }, + }, + }, + }, + } + + return s +} From 5c121373ed7f5ad1b0b0d5b89bc06261761d307b Mon Sep 17 00:00:00 2001 From: Marius Poke Date: Tue, 11 Jun 2024 15:44:37 +0200 Subject: [PATCH 061/102] docs: update releases (bots and docs) (#1948) * update bots * update releases and features --- .github/dependabot.yml | 20 -------------------- .mergify.yml | 16 ---------------- FEATURES.md | 33 +++++++++++++++++---------------- RELEASES.md | 35 +++++++++++++---------------------- 4 files changed, 30 insertions(+), 74 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0a55a2d111..586ef97a95 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -18,26 +18,6 @@ updates: labels: - dependencies - - package-ecosystem: gomod - directory: "/" - schedule: - interval: daily - target-branch: "release/v4.1.x" - # Only allow automated security-related dependency updates on release branches. - open-pull-requests-limit: 0 - labels: - - dependencies - - - package-ecosystem: gomod - directory: "/" - schedule: - interval: daily - target-branch: "release/v4.1.x-lsm" - # Only allow automated security-related dependency updates on release branches. - open-pull-requests-limit: 0 - labels: - - dependencies - - package-ecosystem: gomod directory: "/" schedule: diff --git a/.mergify.yml b/.mergify.yml index d373fe3fb9..18697f0bf4 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -10,22 +10,6 @@ queue_rules: - "#approved-reviews-by>1" pull_request_rules: - - name: Backport patches to the release/v4.1.x branch - conditions: - - base=main - - label=A:backport/v4.1.x - actions: - backport: - branches: - - release/v4.1.x - - name: Backport patches to the release/v4.1.x-lsm branch - conditions: - - base=main - - label=A:backport/v4.1.x-lsm - actions: - backport: - branches: - - release/v4.1.x-lsm - name: Backport patches to the release/v4.2.x branch conditions: - base=main diff --git a/FEATURES.md b/FEATURES.md index f9a54e0589..31d4066926 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -2,19 +2,20 @@ The following table indicates the major ICS features available in the [currently active releases](./RELEASES.md#version-matrix): -| Feature | `v3.2.0` | `v3.3.0` | `v3.3.3-lsm` | `v4.0.0` | `v4.1.1` | `v4.1.1-lsm` | -|---------|---------:|---------:|-------------:|---------:|---------:|-------------:| -| [Channel initialization: new chains](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#channel-initialization-new-chains) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Validator set update](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#validator-set-update) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Completion of unbonding operations](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#completion-of-unbonding-operations) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Consumer initiated slashing](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#consumer-initiated-slashing) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Reward distribution](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#reward-distribution) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Consumer chain removal](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/methods.md#consumer-chain-removal) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Key assignment](https://github.com/cosmos/interchain-security/issues/26) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Jail throttling](https://github.com/cosmos/interchain-security/issues/404) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Soft opt-out](https://github.com/cosmos/interchain-security/issues/851) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Channel initialization: existing chains](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#channel-initialization-existing-chains) (aka [Standalone to consumer changeover](https://github.com/cosmos/interchain-security/issues/756)) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Cryptographic verification of equivocation](https://github.com/cosmos/interchain-security/issues/732) | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Jail throttling with retries](https://github.com/cosmos/interchain-security/issues/713) - consumer-side changes | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Jail throttling with retries](https://github.com/cosmos/interchain-security/issues/713) - [provider-side changes](https://github.com/cosmos/interchain-security/issues/1102) | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | -| [ICS epochs](https://cosmos.github.io/interchain-security/adrs/adr-014-epochs) | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | +| Feature | `v3.2.0` | `v4.0.0` | `v4.2.0` | `v4.2.0-lsm` | `v5.0.0` | +|---------|---------:|---------:|---------:|-------------:|---------:| +| [Channel initialization: new chains](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#channel-initialization-new-chains) | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Validator set update](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#validator-set-update) | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Completion of unbonding operations](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#completion-of-unbonding-operations) | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Consumer initiated slashing](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#consumer-initiated-slashing) | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Reward distribution](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#reward-distribution) | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Consumer chain removal](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/methods.md#consumer-chain-removal) | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Key assignment](https://github.com/cosmos/interchain-security/issues/26) | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Jail throttling](https://github.com/cosmos/interchain-security/issues/404) | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Soft opt-out](https://github.com/cosmos/interchain-security/issues/851) | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Channel initialization: existing chains](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#channel-initialization-existing-chains) (aka [Standalone to consumer changeover](https://github.com/cosmos/interchain-security/issues/756)) | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Cryptographic verification of equivocation](https://github.com/cosmos/interchain-security/issues/732) | ❌ | ✅ | ✅ | ✅ | ✅ | +| [Jail throttling with retries](https://github.com/cosmos/interchain-security/issues/713) - consumer-side changes | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Jail throttling with retries](https://github.com/cosmos/interchain-security/issues/713) - [provider-side changes](https://github.com/cosmos/interchain-security/issues/1102) | ❌ | ✅ | ✅ | ✅ | ✅ | +| [ICS epochs](https://cosmos.github.io/interchain-security/adrs/adr-014-epochs) | ❌ | ❌ | ✅ | ✅ | ✅ | +| [Partial Set Security](https://cosmos.github.io/interchain-security/adrs/adr-015-partial-set-security) | ❌ | ❌ | ✅ | ✅ | ❌ | diff --git a/RELEASES.md b/RELEASES.md index b1d3eefa22..0e48b15368 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -7,7 +7,6 @@ - [Stable Release Policy](#stable-release-policy) - [Version Matrix](#version-matrix) - [Backwards Compatibility](#backwards-compatibility) - - [Notes](#notes) ## Semantic Versioning @@ -60,13 +59,12 @@ All missing minor release versions have been discontinued. | Release | End of Life Date | |---------|------------------| | `v3.2.x` | July 10, 2024 | -| `v3.3.x` | July 10, 2024 | -| `v3.3.x-lsm` | July 10, 2024 | | `v4.0.x` | January 24, 2025 | -| `v4.1.x` | January 24, 2025 | +| `v4.2.x` | January 24, 2025 | +| `v5.0.x` | May 9, 2025 | -**Note**: As of [Gaia v15.1.0](https://github.com/cosmos/gaia/releases/tag/v15.1.0), -the Cosmos Hub uses a fork of Cosmos SDK ([v0.47.10-ics-lsm](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.47.10-ics-lsm)) +**Note**: As of [Gaia v17.2.0](https://github.com/cosmos/gaia/releases/tag/v17.2.0), +the Cosmos Hub uses a fork of Cosmos SDK ([v0.47.15-ics-lsm](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.47.15-ics-lsm)) that contains the Liquid Staking Module (LSM). This means the Cosmos Hub requires a fork of ICS. This fork is maintained by the development team and released using the `-lsm` prefix. @@ -79,11 +77,10 @@ Versions of Golang, IBC, Cosmos SDK and CometBFT used by ICS in the currently ac | ICS | Golang | IBC | Cosmos SDK | CometBFT | Note | |-----|--------|-----|------------|----------|------| | [v3.2.0](https://github.com/cosmos/interchain-security/releases/tag/v3.2.0) | 1.20 | v7.3.0 | v0.47.5 | v0.37.2 | -| [v3.3.0](https://github.com/cosmos/interchain-security/releases/tag/v3.3.0) | 1.20 | v7.3.0 | v0.47.5 | v0.37.2 | -| [v3.3.3-lsm](https://github.com/cosmos/interchain-security/releases/tag/v3.3.3-lsm) | 1.20 | v7.3.1 | v0.47.10-ics-lsm | v0.37.4 | Provider only (Cosmos Hub specific) | | [v4.0.0](https://github.com/cosmos/interchain-security/releases/tag/v4.0.0) | 1.21 | v7.3.1 | v0.47.7 | v0.37.4 | Provider on >= v4.0.0 backwards compatible with consumers >= v3.2.0 | -| [v4.1.1](https://github.com/cosmos/interchain-security/releases/tag/v4.1.1) | 1.21 | v7.4.0 | v0.47.10 | v0.37.4 | -| [v4.1.1-lsm](https://github.com/cosmos/interchain-security/releases/tag/v4.1.1-lsm) | 1.21 | v7.4.0 | v0.47.12-ics-lsm | v0.37.4 | Provider only (Cosmos Hub specific) | +| [v4.2.0](https://github.com/cosmos/interchain-security/releases/tag/v4.2.0) | 1.21 | v7.4.0 | v0.47.11 | v0.37.6 | +| [v4.2.0-lsm](https://github.com/cosmos/interchain-security/releases/tag/v4.2.0-lsm) | 1.21 | v7.4.0 | v0.47.13-ics-lsm | v0.37.6 | Provider only (Cosmos Hub specific) | +| [v5.0.0](https://github.com/cosmos/interchain-security/releases/tag/v5.0.0) | 1.21 | v8.1.0 | v0.50.4 | v0.38.5 | **Note:** For a list of major ICS features available in the currently active releases, see [FEATURES.md](./FEATURES.md). @@ -93,15 +90,9 @@ A MAJOR version of ICS will always be backwards compatible with the previous MAJ The following table indicates the compatibility of currently active releases: -| Consumer | Provider | `v3.2.0` | `v3.3.0` | `v3.3.3-lsm` | `v4.0.0` | `v4.1.1-lsm` | -|----------|----------|----------|----------|--------------|----------|--------------| -| `v3.2.0` || ✅ | ✅ (1) | ✅ | ✅ | ✅ | -| `v3.3.0` || ✅ (1) | ✅ | ✅ | ✅ | ✅ | -| `v4.0.0` || ✅ (1)| ✅ (1)| ✅ (1) | ✅ | ✅ | -| `v4.1.1` || ✅ (1)| ✅ (1)| ✅ (1) | ✅ | ✅ | - -#### Notes - -The following adjustments must be made to the CCV consumer genesis state that is obtained from the provider chain after the spawn time is reached in order for the consumer chain to start without errors. - -- (1) Use `interchain-security-cd genesis transform` to transform the consumer genesis file obtained from the provider. \ No newline at end of file +| Consumer | Provider | `v4.2.0-lsm` | +|----------|----------|--------------| +| `v3.2.0` || ✅ | +| `v4.0.0` || ✅ | +| `v4.2.0` || ✅ | +| `v5.0.0` || ✅ | \ No newline at end of file From 477cce2cc3c98634ee6b3caa92f9aff0ab83c6ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jun 2024 13:46:24 +0000 Subject: [PATCH 062/102] build(deps): bump docker/build-push-action from 5.3.0 to 5.4.0 (#1954) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.3.0 to 5.4.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/2cdde995de11925a030ce8070c3d77a52ffcf1c0...ca052bb54ab0790a636c9b5f226502c73d547a25) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index bb9c86a3f9..091a4fb1eb 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -80,7 +80,7 @@ jobs: # https://github.com/docker/build-push-action - name: Build and push Docker image id: build-and-push - uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 #v5.3.0 + uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 #v5.4.0 with: context: . file: ./Dockerfile From 8955fcb22b1c13d68d1a9ad016038206c0aa847d Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Wed, 12 Jun 2024 09:10:28 +0200 Subject: [PATCH 063/102] refactor: use IterateLastValidatorPowers instead of GetLastValidators (#1953) * Add skeleton for GetLastValidators wrapper * Fix unit tests * Correct comment * Log error messages if validators are not found * Change AnyTimes to more specific Times(1) * Instantiate slices with their max length and truncate * Remove GetLastValidators from expectation * Remove GetLastValidators call in consumer * Move GetLastBondedValidators to validator_set_updates * Add comment on iteration loop --- tests/integration/unbonding.go | 11 ++--- testutil/ibc_testing/generic_setup.go | 2 +- testutil/keeper/expectations.go | 33 +++++++++++++++ testutil/keeper/mocks.go | 42 ------------------- testutil/keeper/unit_test_helpers.go | 4 +- x/ccv/consumer/keeper/changeover_test.go | 16 ++++--- x/ccv/consumer/keeper/keeper.go | 8 +++- x/ccv/consumer/keeper/keeper_test.go | 11 +++-- x/ccv/provider/keeper/grpc_query.go | 6 +-- x/ccv/provider/keeper/grpc_query_test.go | 12 +++--- x/ccv/provider/keeper/partial_set_security.go | 3 +- .../keeper/partial_set_security_test.go | 2 +- x/ccv/provider/keeper/proposal.go | 2 +- x/ccv/provider/keeper/proposal_test.go | 16 +++---- x/ccv/provider/keeper/relay.go | 2 +- x/ccv/provider/keeper/relay_test.go | 8 ++-- x/ccv/provider/keeper/validator_set_update.go | 7 ++++ x/ccv/provider/proposal_handler_test.go | 5 ++- x/ccv/types/expected_keepers.go | 1 - x/ccv/types/utils.go | 38 +++++++++++++++++ 20 files changed, 143 insertions(+), 86 deletions(-) diff --git a/tests/integration/unbonding.go b/tests/integration/unbonding.go index a8398843aa..e7af063d7b 100644 --- a/tests/integration/unbonding.go +++ b/tests/integration/unbonding.go @@ -467,13 +467,14 @@ func (s *CCVTestSuite) TestRedelegationProviderFirst() { // during the staking module EndBlock. func (s *CCVTestSuite) TestTooManyLastValidators() { sk := s.providerApp.GetTestStakingKeeper() + pk := s.providerApp.GetProviderKeeper() // get current staking params p := sk.GetParams(s.providerCtx()) // get validators, which are all active at the moment vals := sk.GetAllValidators(s.providerCtx()) - s.Require().Equal(len(vals), len(sk.GetLastValidators(s.providerCtx()))) + s.Require().Equal(len(vals), len(pk.GetLastBondedValidators(s.providerCtx()))) // jail a validator val := vals[0] @@ -482,17 +483,17 @@ func (s *CCVTestSuite) TestTooManyLastValidators() { sk.Jail(s.providerCtx(), consAddr) // save the current number of bonded vals - lastVals := sk.GetLastValidators(s.providerCtx()) + lastVals := pk.GetLastBondedValidators(s.providerCtx()) // pass one block to apply the validator set changes // (calls ApplyAndReturnValidatorSetUpdates in the the staking module EndBlock) s.providerChain.NextBlock() // verify that the number of bonded validators is decreased by one - s.Require().Equal(len(lastVals)-1, len(sk.GetLastValidators(s.providerCtx()))) + s.Require().Equal(len(lastVals)-1, len(pk.GetLastBondedValidators(s.providerCtx()))) // update maximum validator to equal the number of bonded validators - p.MaxValidators = uint32(len(sk.GetLastValidators(s.providerCtx()))) + p.MaxValidators = uint32(len(pk.GetLastBondedValidators(s.providerCtx()))) sk.SetParams(s.providerCtx(), p) // pass one block to apply validator set changes @@ -508,5 +509,5 @@ func (s *CCVTestSuite) TestTooManyLastValidators() { // ApplyAndReturnValidatorSetUpdates where the staking module has a inconsistent state s.Require().NotPanics(s.providerChain.NextBlock) s.Require().NotPanics(func() { sk.ApplyAndReturnValidatorSetUpdates(s.providerCtx()) }) - s.Require().NotPanics(func() { sk.GetLastValidators(s.providerCtx()) }) + s.Require().NotPanics(func() { pk.GetLastBondedValidators(s.providerCtx()) }) } diff --git a/testutil/ibc_testing/generic_setup.go b/testutil/ibc_testing/generic_setup.go index 09dfa62140..21e9d1326f 100644 --- a/testutil/ibc_testing/generic_setup.go +++ b/testutil/ibc_testing/generic_setup.go @@ -143,7 +143,7 @@ func AddConsumer[Tp testutil.ProviderApp, Tc testutil.ConsumerApp]( prop.Top_N = consumerTopNParams[index] // isn't used in CreateConsumerClient // opt-in all validators - for _, v := range providerApp.GetTestStakingKeeper().GetLastValidators(providerChain.GetContext()) { + for _, v := range providerKeeper.GetLastBondedValidators(providerChain.GetContext()) { consAddr, _ := v.GetConsAddr() providerKeeper.SetOptedIn(providerChain.GetContext(), chainID, providertypes.NewProviderConsAddress(consAddr)) } diff --git a/testutil/keeper/expectations.go b/testutil/keeper/expectations.go index 8a010ce405..0c6db62cf0 100644 --- a/testutil/keeper/expectations.go +++ b/testutil/keeper/expectations.go @@ -217,3 +217,36 @@ func GetMocksForSlashValidator( Times(1), } } + +// SetupMocksForLastBondedValidatorsExpectation sets up the expectation for the `IterateLastValidatorPowers` `MaxValidators`, and `GetValidator` methods of the `mockStakingKeeper` object. +// These are needed in particular when calling `GetLastBondedValidators` from the provider keeper. +// Times is the number of times the expectation should be called. Provide -1 for `AnyTimes“. +func SetupMocksForLastBondedValidatorsExpectation(mockStakingKeeper *MockStakingKeeper, maxValidators uint32, vals []stakingtypes.Validator, powers []int64, times int) { + iteratorCall := mockStakingKeeper.EXPECT().IterateLastValidatorPowers(gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx sdk.Context, cb func(sdk.ValAddress, int64) bool) { + for i, val := range vals { + if stop := cb(sdk.ValAddress(val.OperatorAddress), powers[i]); stop { + break + } + } + }) + maxValidatorsCall := mockStakingKeeper.EXPECT().MaxValidators(gomock.Any()).Return(maxValidators) + + if times == -1 { + iteratorCall.AnyTimes() + maxValidatorsCall.AnyTimes() + } else { + iteratorCall.Times(times) + maxValidatorsCall.Times(times) + } + + // set up mocks for GetValidator calls + for _, val := range vals { + getValCall := mockStakingKeeper.EXPECT().GetValidator(gomock.Any(), sdk.ValAddress(val.OperatorAddress)).Return(val, true) + if times == -1 { + getValCall.AnyTimes() + } else { + getValCall.Times(times) + } + } +} diff --git a/testutil/keeper/mocks.go b/testutil/keeper/mocks.go index 0ad09a0be6..783144eb49 100644 --- a/testutil/keeper/mocks.go +++ b/testutil/keeper/mocks.go @@ -105,34 +105,6 @@ func (mr *MockStakingKeeperMockRecorder) GetLastValidatorPower(ctx, operator int return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastValidatorPower", reflect.TypeOf((*MockStakingKeeper)(nil).GetLastValidatorPower), ctx, operator) } -// GetLastValidators mocks base method. -func (m *MockStakingKeeper) GetLastValidators(ctx types0.Context) []types5.Validator { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetLastValidators", ctx) - ret0, _ := ret[0].([]types5.Validator) - return ret0 -} - -// GetLastValidators indicates an expected call of GetLastValidators. -func (mr *MockStakingKeeperMockRecorder) GetLastValidators(ctx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastValidators", reflect.TypeOf((*MockStakingKeeper)(nil).GetLastValidators), ctx) -} - -// GetParams mocks base method. -func (m *MockStakingKeeper) GetParams(ctx types0.Context) types5.Params { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetParams", ctx) - ret0, _ := ret[0].(types5.Params) - return ret0 -} - -// GetParams indicates an expected call of GetParams. -func (mr *MockStakingKeeperMockRecorder) GetParams(ctx interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetParams", reflect.TypeOf((*MockStakingKeeper)(nil).GetParams), ctx) -} - // GetRedelegationByUnbondingID mocks base method. func (m *MockStakingKeeper) GetRedelegationByUnbondingID(ctx types0.Context, id uint64) (types5.Redelegation, bool) { m.ctrl.T.Helper() @@ -371,20 +343,6 @@ func (mr *MockStakingKeeperMockRecorder) PutUnbondingOnHold(ctx, id interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutUnbondingOnHold", reflect.TypeOf((*MockStakingKeeper)(nil).PutUnbondingOnHold), ctx, id) } -// SetParams mocks base method. -func (m *MockStakingKeeper) SetParams(ctx types0.Context, p types5.Params) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SetParams", ctx, p) - ret0, _ := ret[0].(error) - return ret0 -} - -// SetParams indicates an expected call of SetParams. -func (mr *MockStakingKeeperMockRecorder) SetParams(ctx, p interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetParams", reflect.TypeOf((*MockStakingKeeper)(nil).SetParams), ctx, p) -} - // Slash mocks base method. func (m *MockStakingKeeper) Slash(arg0 types0.Context, arg1 types0.ConsAddress, arg2, arg3 int64, arg4 types0.Dec) math.Int { m.ctrl.T.Helper() diff --git a/testutil/keeper/unit_test_helpers.go b/testutil/keeper/unit_test_helpers.go index ef208cb907..65cec0b784 100644 --- a/testutil/keeper/unit_test_helpers.go +++ b/testutil/keeper/unit_test_helpers.go @@ -215,7 +215,9 @@ func SetupForStoppingConsumerChain(t *testing.T, ctx sdk.Context, providerKeeper *providerkeeper.Keeper, mocks MockedKeepers, ) { t.Helper() - mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Times(1) + + SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 1, []stakingtypes.Validator{}, []int64{}, 1) + expectations := GetMocksForCreateConsumerClient(ctx, &mocks, "chainID", clienttypes.NewHeight(4, 5)) expectations = append(expectations, GetMocksForSetConsumerChain(ctx, &mocks, "chainID")...) diff --git a/x/ccv/consumer/keeper/changeover_test.go b/x/ccv/consumer/keeper/changeover_test.go index c431f43477..ba70e56041 100644 --- a/x/ccv/consumer/keeper/changeover_test.go +++ b/x/ccv/consumer/keeper/changeover_test.go @@ -3,7 +3,6 @@ package keeper_test import ( "testing" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" sdkcryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" @@ -30,6 +29,8 @@ func TestChangeoverToConsumer(t *testing.T) { cIds[4].SDKStakingValidator(), } + powers := []int64{55, 87324, 2, 42389479, 9089080} + // Instantiate 5 ics val updates for use in test initialValUpdates := []abci.ValidatorUpdate{ {Power: 55, PubKey: cIds[5].TMProtoCryptoPublicKey()}, @@ -41,7 +42,7 @@ func TestChangeoverToConsumer(t *testing.T) { testCases := []struct { name string - // Last standalone validators that will be mock returned from stakingKeeper.GetLastValidators() + // Last standalone validators that will be mock returned from consumerKeeper.GetLastBondedValidators() lastSovVals []stakingtypes.Validator // Val updates corresponding to initial valset set for ccv set initGenesis initialValUpdates []abci.ValidatorUpdate @@ -100,10 +101,13 @@ func TestChangeoverToConsumer(t *testing.T) { // Set initial valset, as would be done in InitGenesis consumerKeeper.SetInitialValSet(ctx, tc.initialValUpdates) - // Setup mocked return value for stakingKeeper.GetLastValidators() - gomock.InOrder( - mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Return(tc.lastSovVals), - ) + // Setup mocked return value for consumerkeeper.GetLastBondedValidators() + uthelpers.SetupMocksForLastBondedValidatorsExpectation( + mocks.MockStakingKeeper, + 180, // max validators + tc.lastSovVals, + powers, + -1) // any times // Add ref to standalone staking keeper consumerKeeper.SetStandaloneStakingKeeper(mocks.MockStakingKeeper) diff --git a/x/ccv/consumer/keeper/keeper.go b/x/ccv/consumer/keeper/keeper.go index b8751344b8..9dde578a4e 100644 --- a/x/ccv/consumer/keeper/keeper.go +++ b/x/ccv/consumer/keeper/keeper.go @@ -331,7 +331,7 @@ func (k Keeper) GetLastStandaloneValidators(ctx sdk.Context) []stakingtypes.Vali if !k.IsPreCCV(ctx) || k.standaloneStakingKeeper == nil { panic("cannot get last standalone validators if not in pre-ccv state, or if standalone staking keeper is nil") } - return k.standaloneStakingKeeper.GetLastValidators(ctx) + return k.GetLastBondedValidators(ctx) } // GetElapsedPacketMaturityTimes returns a slice of already elapsed PacketMaturityTimes, sorted by maturity times, @@ -690,3 +690,9 @@ func (k Keeper) IsPrevStandaloneChain(ctx sdk.Context) bool { store := ctx.KVStore(k.storeKey) return store.Has(types.PrevStandaloneChainKey()) } + +// GetLastBondedValidators iterates the last validator powers in the staking module +// and returns the first MaxValidators many validators with the largest powers. +func (k Keeper) GetLastBondedValidators(ctx sdk.Context) []stakingtypes.Validator { + return ccv.GetLastBondedValidatorsUtil(ctx, k.standaloneStakingKeeper, k.Logger(ctx)) +} diff --git a/x/ccv/consumer/keeper/keeper_test.go b/x/ccv/consumer/keeper/keeper_test.go index 19856211b5..4b9a59fbba 100644 --- a/x/ccv/consumer/keeper/keeper_test.go +++ b/x/ccv/consumer/keeper/keeper_test.go @@ -184,11 +184,14 @@ func TestGetLastSovereignValidators(t *testing.T) { cId1 := crypto.NewCryptoIdentityFromIntSeed(11) val := cId1.SDKStakingValidator() val.Description.Moniker = "sanity check this is the correctly serialized val" - gomock.InOrder( - mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Return([]stakingtypes.Validator{ - val, - }), + testkeeper.SetupMocksForLastBondedValidatorsExpectation( + mocks.MockStakingKeeper, + 180, + []stakingtypes.Validator{val}, + []int64{1000}, + 1, ) + lastSovVals := ck.GetLastStandaloneValidators(ctx) require.Equal(t, []stakingtypes.Validator{val}, lastSovVals) require.Equal(t, "sanity check this is the correctly serialized val", diff --git a/x/ccv/provider/keeper/grpc_query.go b/x/ccv/provider/keeper/grpc_query.go index ee00556f06..4b6fbd0b90 100644 --- a/x/ccv/provider/keeper/grpc_query.go +++ b/x/ccv/provider/keeper/grpc_query.go @@ -70,7 +70,7 @@ func (k Keeper) GetConsumerChain(ctx sdk.Context, chainID string) (types.Chain, // Get MinPowerInTop_N var minPowerInTopN int64 if found && topN > 0 { - res, err := k.ComputeMinPowerToOptIn(ctx, k.stakingKeeper.GetLastValidators(ctx), topN) + res, err := k.ComputeMinPowerToOptIn(ctx, k.GetLastBondedValidators(ctx), topN) if err != nil { return types.Chain{}, fmt.Errorf("failed to compute min power to opt in for chain (%s): %w", chainID, err) } @@ -381,7 +381,7 @@ func (k Keeper) hasToValidate( } // if the validator was not part of the last epoch, check if the validator is going to be part of te next epoch - bondedValidators := k.stakingKeeper.GetLastValidators(ctx) + bondedValidators := k.GetLastBondedValidators(ctx) if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { // in a Top-N chain, we automatically opt in all validators that belong to the top N minPower, err := k.ComputeMinPowerToOptIn(ctx, bondedValidators, topN) @@ -395,7 +395,7 @@ func (k Keeper) hasToValidate( // if the validator is opted in and belongs to the validators of the next epoch, then if nothing changes // the validator would have to validate in the next epoch if k.IsOptedIn(ctx, chainID, provAddr) { - nextValidators := k.ComputeNextValidators(ctx, chainID, k.stakingKeeper.GetLastValidators(ctx)) + nextValidators := k.ComputeNextValidators(ctx, chainID, bondedValidators) for _, v := range nextValidators { consAddr := sdk.ConsAddress(v.ProviderConsAddr) if provAddr.ToSdkConsAddr().Equals(consAddr) { diff --git a/x/ccv/provider/keeper/grpc_query_test.go b/x/ccv/provider/keeper/grpc_query_test.go index 4f3825238f..4d59a140b5 100644 --- a/x/ccv/provider/keeper/grpc_query_test.go +++ b/x/ccv/provider/keeper/grpc_query_test.go @@ -184,7 +184,7 @@ func TestQueryConsumerChainsValidatorHasToValidate(t *testing.T) { valConsAddr, _ := val.GetConsAddr() providerAddr := types.NewProviderConsAddress(valConsAddr) mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valConsAddr).Return(val, true).AnyTimes() - mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Return([]stakingtypes.Validator{val}).AnyTimes() + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 1, []stakingtypes.Validator{val}, []int64{1}, -1) // -1 to allow the calls "AnyTimes" req := types.QueryConsumerChainsValidatorHasToValidateRequest{ ProviderAddress: providerAddr.String(), @@ -203,14 +203,15 @@ func TestQueryConsumerChainsValidatorHasToValidate(t *testing.T) { ConsumerPublicKey: &crypto.PublicKey{ Sum: &crypto.PublicKey_Ed25519{ Ed25519: []byte{1}, - }}}) + }, + }, + }) // set `providerAddr` as an opted-in validator on "chain3" pk.SetOptedIn(ctx, "chain3", providerAddr) // `providerAddr` has to validate "chain1" because it is a consumer validator in this chain, as well as "chain3" - // because it opted in, in "chain3" and `providerAddr` belongs to the bonded validators (see the mocking of `GetLastValidators` - // above) + // because it opted in, in "chain3" and `providerAddr` belongs to the bonded validators expectedChains := []string{"chain1", "chain3"} res, err := pk.QueryConsumerChainsValidatorHasToValidate(ctx, &req) @@ -268,7 +269,8 @@ func TestGetConsumerChain(t *testing.T) { {OperatorAddress: "cosmosvaloper1tflk30mq5vgqjdly92kkhhq3raev2hnz6eete3"}, // 500 power } powers := []int64{50, 150, 300, 500} // sum = 1000 - mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Return(vals).AnyTimes() + maxValidators := uint32(180) + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, maxValidators, vals, powers, -1) // -1 to allow the calls "AnyTimes" for i, val := range vals { mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), val.GetOperator()).Return(powers[i]).AnyTimes() diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index 1810bcc0f8..a48e70e80d 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -63,8 +63,7 @@ func (k Keeper) HandleOptOut(ctx sdk.Context, chainID string, providerAddr types "validator with consensus address %s could not be found", providerAddr.ToSdkConsAddr()) } power := k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()) - minPowerToOptIn, err := k.ComputeMinPowerToOptIn(ctx, k.stakingKeeper.GetLastValidators(ctx), topN) - + minPowerToOptIn, err := k.ComputeMinPowerToOptIn(ctx, k.GetLastBondedValidators(ctx), topN) if err != nil { k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err) return errorsmod.Wrapf( diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index cb1fbab9d4..4209f7f6ba 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -129,7 +129,7 @@ func TestHandleOptOutFromTopNChain(t *testing.T) { valDConsAddr, _ := valD.GetConsAddr() mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valDConsAddr).Return(valD, true).AnyTimes() - mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Return([]stakingtypes.Validator{valA, valB, valC, valD}).AnyTimes() + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 4, []stakingtypes.Validator{valA, valB, valC, valD}, []int64{1, 2, 3, 4}, -1) // -1 to allow mocks AnyTimes // opt in all validators providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(valAConsAddr)) diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 34bb5b6971..bbb1858fa4 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -293,7 +293,7 @@ func (k Keeper) MakeConsumerGenesis( } // get the bonded validators from the staking module - bondedValidators := k.stakingKeeper.GetLastValidators(ctx) + bondedValidators := k.GetLastBondedValidators(ctx) if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { // in a Top-N chain, we automatically opt in all validators that belong to the top N diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index c561960a49..a7e26534b5 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -3,11 +3,12 @@ package keeper_test import ( "bytes" "encoding/json" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "sort" "testing" "time" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" _go "github.com/cosmos/ics23/go" @@ -116,7 +117,7 @@ func TestHandleConsumerAdditionProposal(t *testing.T) { if tc.expAppendProp { // Mock calls are only asserted if we expect a client to be created. - mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Times(1) + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, []int64{}, 1) // returns empty validator set gomock.InOrder( testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, tc.prop.ChainId, clienttypes.NewHeight(2, 3))..., ) @@ -160,7 +161,7 @@ func TestCreateConsumerClient(t *testing.T) { description: "No state mutation, new client should be created", setup: func(providerKeeper *providerkeeper.Keeper, ctx sdk.Context, mocks *testkeeper.MockedKeepers) { // Valid client creation is asserted with mock expectations here - mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Times(1) + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, []int64{}, 1) // returns empty validator set gomock.InOrder( testkeeper.GetMocksForCreateConsumerClient(ctx, mocks, "chainID", clienttypes.NewHeight(4, 5))..., ) @@ -176,7 +177,7 @@ func TestCreateConsumerClient(t *testing.T) { mocks.MockStakingKeeper.EXPECT().UnbondingTime(gomock.Any()).Times(0) mocks.MockClientKeeper.EXPECT().CreateClient(gomock.Any(), gomock.Any(), gomock.Any()).Times(0) mocks.MockClientKeeper.EXPECT().GetSelfConsensusState(gomock.Any(), gomock.Any()).Times(0) - mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Times(0) + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, []int64{}, 0) // returns empty validator set }, expClientCreated: false, }, @@ -848,7 +849,7 @@ func TestMakeConsumerGenesis(t *testing.T) { // ctx = ctx.WithChainID("testchain1") // chainID is obtained from ctx ctx = ctx.WithBlockHeight(5) // RevisionHeight obtained from ctx - mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Times(1) + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, []int64{}, 1) gomock.InOrder(testkeeper.GetMocksForMakeConsumerGenesis(ctx, &mocks, 1814400000000000)...) // matches params from jsonString @@ -1094,7 +1095,8 @@ func TestBeginBlockInit(t *testing.T) { // opt in a sample validator so the chain's proposal can successfully execute validator := cryptotestutil.NewCryptoIdentityFromIntSeed(0).SDKStakingValidator() consAddr, _ := validator.GetConsAddr() - mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Return([]stakingtypes.Validator{validator}).AnyTimes() + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 1, []stakingtypes.Validator{validator}, []int64{0}, -1) // -1 to allow any number of calls + mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), validator.GetOperator()).Return(int64(1)).AnyTimes() providerKeeper.SetOptedIn(ctx, pendingProps[4].ChainId, providertypes.NewProviderConsAddress(consAddr)) @@ -1199,7 +1201,7 @@ func TestBeginBlockCCR(t *testing.T) { expectations := []*gomock.Call{} for _, prop := range pendingProps { // A consumer chain is setup corresponding to each prop, making these mocks necessary - mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Times(1) + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, []int64{}, 1) expectations = append(expectations, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, prop.ChainId, clienttypes.NewHeight(2, 3))...) expectations = append(expectations, testkeeper.GetMocksForSetConsumerChain(ctx, &mocks, prop.ChainId)...) diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index 852b70928b..f9ec094a70 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -218,7 +218,7 @@ func (k Keeper) QueueVSCPackets(ctx sdk.Context) { valUpdateID := k.GetValidatorSetUpdateId(ctx) // current valset update ID // get the bonded validators from the staking module - bondedValidators := k.stakingKeeper.GetLastValidators(ctx) + bondedValidators := k.GetLastBondedValidators(ctx) for _, chainID := range k.GetAllRegisteredConsumerChainIDs(ctx) { currentValidators := k.GetConsumerValSet(ctx, chainID) diff --git a/x/ccv/provider/keeper/relay_test.go b/x/ccv/provider/keeper/relay_test.go index 5f74d93424..a4fe6e60ff 100644 --- a/x/ccv/provider/keeper/relay_test.go +++ b/x/ccv/provider/keeper/relay_test.go @@ -70,7 +70,7 @@ func TestQueueVSCPackets(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mocks := testkeeper.NewMockedKeepers(ctrl) - mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Times(1) + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, []int64{}, 1) pk := testkeeper.NewInMemProviderKeeper(keeperParams, mocks) // no-op if tc.packets is empty @@ -760,14 +760,16 @@ func TestEndBlockVSU(t *testing.T) { // create 4 sample lastValidators var lastValidators []stakingtypes.Validator var valAddresses []sdk.ValAddress + var powers []int64 for i := 0; i < 4; i++ { validator := crypto.NewCryptoIdentityFromIntSeed(i).SDKStakingValidator() lastValidators = append(lastValidators, validator) valAddresses = append(valAddresses, validator.GetOperator()) mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), validator.GetOperator()).Return(int64(i + 1)).AnyTimes() + powers = append(powers, int64(i+1)) } - mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Return(lastValidators).AnyTimes() + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 5, lastValidators, powers, -1) // set a sample client for a consumer chain so that `GetAllConsumerChains` in `QueueVSCPackets` iterates at least once providerKeeper.SetConsumerClientId(ctx, chainID, "clientID") @@ -822,7 +824,7 @@ func TestQueueVSCPacketsWithPowerCapping(t *testing.T) { valEPubKey, _ := valE.TmConsPublicKey() mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valEConsAddr).Return(valE, true).AnyTimes() - mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Return([]stakingtypes.Validator{valA, valB, valC, valD, valE}).AnyTimes() + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 5, []stakingtypes.Validator{valA, valB, valC, valD, valE}, []int64{1, 3, 4, 8, 16}, -1) // add a consumer chain providerKeeper.SetConsumerClientId(ctx, "chainID", "clientID") diff --git a/x/ccv/provider/keeper/validator_set_update.go b/x/ccv/provider/keeper/validator_set_update.go index 7fa175e1b9..9ba2c961ab 100644 --- a/x/ccv/provider/keeper/validator_set_update.go +++ b/x/ccv/provider/keeper/validator_set_update.go @@ -9,6 +9,7 @@ import ( abci "github.com/cometbft/cometbft/abci/types" "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" + ccv "github.com/cosmos/interchain-security/v4/x/ccv/types" ) // SetConsumerValidator sets provided consumer `validator` on the consumer chain with `chainID` @@ -189,3 +190,9 @@ func (k Keeper) FilterValidators( return nextValidators } + +// GetLastBondedValidators iterates the last validator powers in the staking module +// and returns the first MaxValidators many validators with the largest powers. +func (k Keeper) GetLastBondedValidators(ctx sdk.Context) []stakingtypes.Validator { + return ccv.GetLastBondedValidatorsUtil(ctx, k.stakingKeeper, k.Logger(ctx)) +} diff --git a/x/ccv/provider/proposal_handler_test.go b/x/ccv/provider/proposal_handler_test.go index 8d5b696b40..cd4395e38d 100644 --- a/x/ccv/provider/proposal_handler_test.go +++ b/x/ccv/provider/proposal_handler_test.go @@ -4,13 +4,14 @@ import ( "testing" "time" + sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" - sdk "github.com/cosmos/cosmos-sdk/types" distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" testkeeper "github.com/cosmos/interchain-security/v4/testutil/keeper" "github.com/cosmos/interchain-security/v4/x/ccv/provider" @@ -96,7 +97,7 @@ func TestProviderProposalHandler(t *testing.T) { // Mock expectations depending on expected outcome switch { case tc.expValidConsumerAddition: - mocks.MockStakingKeeper.EXPECT().GetLastValidators(gomock.Any()).Times(1) + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 1, []stakingtypes.Validator{}, []int64{}, 1) gomock.InOrder(testkeeper.GetMocksForCreateConsumerClient( ctx, &mocks, "chainID", clienttypes.NewHeight(2, 3), )...) diff --git a/x/ccv/types/expected_keepers.go b/x/ccv/types/expected_keepers.go index 77ac40eecf..f1c543405e 100644 --- a/x/ccv/types/expected_keepers.go +++ b/x/ccv/types/expected_keepers.go @@ -52,7 +52,6 @@ type StakingKeeper interface { Delegation(ctx sdk.Context, addr sdk.AccAddress, valAddr sdk.ValAddress) stakingtypes.DelegationI MaxValidators(ctx sdk.Context) uint32 GetLastTotalPower(ctx sdk.Context) math.Int - GetLastValidators(ctx sdk.Context) (validators []stakingtypes.Validator) BondDenom(ctx sdk.Context) (res string) GetUnbondingDelegationsFromValidator(ctx sdk.Context, valAddr sdk.ValAddress) (ubds []stakingtypes.UnbondingDelegation) GetRedelegationsFromSrcValidator(ctx sdk.Context, valAddr sdk.ValAddress) (reds []stakingtypes.Redelegation) diff --git a/x/ccv/types/utils.go b/x/ccv/types/utils.go index ae85240256..cb4e86d6b2 100644 --- a/x/ccv/types/utils.go +++ b/x/ccv/types/utils.go @@ -12,10 +12,12 @@ import ( host "github.com/cosmos/ibc-go/v7/modules/core/24-host" errorsmod "cosmossdk.io/errors" + "github.com/cometbft/cometbft/libs/log" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/bech32" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" abci "github.com/cometbft/cometbft/abci/types" tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" @@ -123,3 +125,39 @@ func GetConsAddrFromBech32(bech32str string) (sdk.ConsAddress, error) { } return sdk.ConsAddress(addr), nil } + +func GetLastBondedValidatorsUtil(ctx sdk.Context, stakingKeeper StakingKeeper, logger log.Logger) []stakingtypes.Validator { + maxVals := stakingKeeper.MaxValidators(ctx) + + lastPowers := make([]stakingtypes.LastValidatorPower, maxVals) + + i := 0 + stakingKeeper.IterateLastValidatorPowers(ctx, func(addr sdk.ValAddress, power int64) (stop bool) { + lastPowers[i] = stakingtypes.LastValidatorPower{Address: addr.String(), Power: power} + i++ + return i >= int(maxVals) // stop iteration if true + }) + + // truncate the lastPowers + lastPowers = lastPowers[:i] + + bondedValidators := make([]stakingtypes.Validator, len(lastPowers)) + + for index, p := range lastPowers { + addr, err := sdk.ValAddressFromBech32(p.Address) + if err != nil { + logger.Error("Invalid validator address", "address", p.Address, "error", err) + continue + } + + val, found := stakingKeeper.GetValidator(ctx, addr) + if !found { + logger.Error("Validator not found", "address", addr.String()) + continue + } + + // gather all the bonded validators in order to construct the consumer validator set for consumer chain `chainID` + bondedValidators[index] = val + } + return bondedValidators +} From 515918aabf6e5579b4eeb1f72c5fcbdbb8c8d808 Mon Sep 17 00:00:00 2001 From: bernd-m <43466467+bermuell@users.noreply.github.com> Date: Wed, 12 Jun 2024 09:39:58 +0200 Subject: [PATCH 064/102] ci: fix merge issue on nightly e2e workflow (#1959) * fix merge issue on nightly e2e * addressed comments --- .github/workflows/nightly-e2e.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/nightly-e2e.yml b/.github/workflows/nightly-e2e.yml index e872f10230..9633ff3112 100644 --- a/.github/workflows/nightly-e2e.yml +++ b/.github/workflows/nightly-e2e.yml @@ -327,11 +327,8 @@ jobs: - partial-set-security-validators-power-cap-test - partial-set-security-validators-allowlisted-test - partial-set-security-validators-denylisted-test -<<<<<<< HEAD - - active-set-changes-test -======= - partial-set-security-modification-proposal ->>>>>>> main + - active-set-changes-test if: ${{ failure() }} runs-on: ubuntu-latest steps: From 5d6f31381be879e1fa18c216aa8361c6524974e5 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Wed, 12 Jun 2024 15:30:08 +0200 Subject: [PATCH 065/102] feat!: store the minimal power to be in the top N on EndBlock, instead of computing on-the-fly (#1952) * Store the minimal power among the top N in EndBlock * Finish merge * Fix unit tests * Fix store method for the min power * Fix migration * Revert migration changes * Change comment to proper name for key * Add staking keeper to migration * Revert "Add staking keeper to migration" This reverts commit 575cfd3ccec7732e0d1488d80bda7f6172110cf7. * Rename migration * Update x/ccv/provider/keeper/grpc_query.go * Clean up minimal power in top N on StopConsumerChain * Set min power in consumer modification proposal * Address comments * Use GetLastBondedValidators instead of GetLastValidators * Add migration * Add comment for migration * Improve comment in migration * Handle case where topN is not found * Add test for updating minimum power in top N * Merged tests * Rename updatedMinPower->newUpdatedMinPower * Address comments --- x/ccv/provider/keeper/grpc_query.go | 23 ++++++----- x/ccv/provider/keeper/grpc_query_test.go | 1 + x/ccv/provider/keeper/keeper.go | 39 +++++++++++++++++++ x/ccv/provider/keeper/keeper_test.go | 30 ++++++++++++++ x/ccv/provider/keeper/partial_set_security.go | 21 +++++----- .../keeper/partial_set_security_test.go | 31 ++++++++------- x/ccv/provider/keeper/proposal.go | 34 +++++++++++++--- x/ccv/provider/keeper/relay.go | 5 ++- x/ccv/provider/migrations/migrator.go | 10 ++++- x/ccv/provider/migrations/v6/migrations.go | 33 ++++++++++++++++ x/ccv/provider/module.go | 2 +- x/ccv/provider/types/keys.go | 8 ++++ x/ccv/provider/types/keys_test.go | 1 + 13 files changed, 192 insertions(+), 46 deletions(-) create mode 100644 x/ccv/provider/migrations/v6/migrations.go diff --git a/x/ccv/provider/keeper/grpc_query.go b/x/ccv/provider/keeper/grpc_query.go index 4b6fbd0b90..b1a5b36dcd 100644 --- a/x/ccv/provider/keeper/grpc_query.go +++ b/x/ccv/provider/keeper/grpc_query.go @@ -66,16 +66,15 @@ func (k Keeper) GetConsumerChain(ctx sdk.Context, chainID string) (types.Chain, } topN, found := k.GetTopN(ctx, chainID) + if !found { + k.Logger(ctx).Error("failed to get top N, treating as 0", "chain", chainID) + topN = 0 + } - // Get MinPowerInTop_N - var minPowerInTopN int64 - if found && topN > 0 { - res, err := k.ComputeMinPowerToOptIn(ctx, k.GetLastBondedValidators(ctx), topN) - if err != nil { - return types.Chain{}, fmt.Errorf("failed to compute min power to opt in for chain (%s): %w", chainID, err) - } - minPowerInTopN = res - } else { + // Get the minimal power in the top N for the consumer chain + minPowerInTopN, found := k.GetMinimumPowerInTopN(ctx, chainID) + if !found { + k.Logger(ctx).Error("failed to get minimum power in top N, treating as -1", "chain", chainID) minPowerInTopN = -1 } @@ -384,11 +383,11 @@ func (k Keeper) hasToValidate( bondedValidators := k.GetLastBondedValidators(ctx) if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { // in a Top-N chain, we automatically opt in all validators that belong to the top N - minPower, err := k.ComputeMinPowerToOptIn(ctx, bondedValidators, topN) - if err == nil { + minPower, found := k.GetMinimumPowerInTopN(ctx, chainID) + if found { k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) } else { - k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err) + k.Logger(ctx).Error("did not find min power in top N for chain", "chain", chainID) } } diff --git a/x/ccv/provider/keeper/grpc_query_test.go b/x/ccv/provider/keeper/grpc_query_test.go index 4d59a140b5..befc84a8ea 100644 --- a/x/ccv/provider/keeper/grpc_query_test.go +++ b/x/ccv/provider/keeper/grpc_query_test.go @@ -309,6 +309,7 @@ func TestGetConsumerChain(t *testing.T) { pk.SetTopN(ctx, chainID, topN) pk.SetValidatorSetCap(ctx, chainID, validatorSetCaps[i]) pk.SetValidatorsPowerCap(ctx, chainID, validatorPowerCaps[i]) + pk.SetMinimumPowerInTopN(ctx, chainID, expectedMinPowerInTopNs[i]) for _, addr := range allowlists[i] { pk.SetAllowlist(ctx, chainID, addr) } diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 9b95036dda..5829ebaf61 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -1513,3 +1513,42 @@ func (k Keeper) IsDenylistEmpty(ctx sdk.Context, chainID string) bool { return !iterator.Valid() } + +// SetMinimumPowerInTopN sets the minimum power required for a validator to be in the top N +// for a given consumer chain. +func (k Keeper) SetMinimumPowerInTopN( + ctx sdk.Context, + chainID string, + power int64, +) { + store := ctx.KVStore(k.storeKey) + + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, uint64(power)) + + store.Set(types.MinimumPowerInTopNKey(chainID), buf) +} + +// GetMinimumPowerInTopN returns the minimum power required for a validator to be in the top N +// for a given consumer chain. +func (k Keeper) GetMinimumPowerInTopN( + ctx sdk.Context, + chainID string, +) (int64, bool) { + store := ctx.KVStore(k.storeKey) + buf := store.Get(types.MinimumPowerInTopNKey(chainID)) + if buf == nil { + return 0, false + } + return int64(binary.BigEndian.Uint64(buf)), true +} + +// DeleteMinimumPowerInTopN removes the minimum power required for a validator to be in the top N +// for a given consumer chain. +func (k Keeper) DeleteMinimumPowerInTopN( + ctx sdk.Context, + chainID string, +) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.MinimumPowerInTopNKey(chainID)) +} diff --git a/x/ccv/provider/keeper/keeper_test.go b/x/ccv/provider/keeper/keeper_test.go index f68afc20c2..9a064a0389 100644 --- a/x/ccv/provider/keeper/keeper_test.go +++ b/x/ccv/provider/keeper/keeper_test.go @@ -831,3 +831,33 @@ func TestDenylist(t *testing.T) { require.False(t, providerKeeper.IsDenylisted(ctx, chainID, providerAddr2)) require.True(t, providerKeeper.IsDenylistEmpty(ctx, chainID)) } + +func TestMinimumPowerInTopN(t *testing.T) { + k, ctx, _, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + + chainID := "testChain" + minPower := int64(1000) + + // Set the minimum power in top N + k.SetMinimumPowerInTopN(ctx, chainID, minPower) + + // Retrieve the minimum power in top N + actualMinPower, found := k.GetMinimumPowerInTopN(ctx, chainID) + require.True(t, found) + require.Equal(t, minPower, actualMinPower) + + // Update the minimum power + newMinPower := int64(2000) + k.SetMinimumPowerInTopN(ctx, chainID, newMinPower) + + // Retrieve the updated minimum power in top N + newActualMinPower, found := k.GetMinimumPowerInTopN(ctx, chainID) + require.True(t, found) + require.Equal(t, newMinPower, newActualMinPower) + + // Test when the chain ID does not exist + nonExistentChainID := "nonExistentChain" + nonExistentMinPower, found := k.GetMinimumPowerInTopN(ctx, nonExistentChainID) + require.False(t, found) + require.Equal(t, int64(0), nonExistentMinPower) +} diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index a48e70e80d..fe7a18714b 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -63,21 +63,18 @@ func (k Keeper) HandleOptOut(ctx sdk.Context, chainID string, providerAddr types "validator with consensus address %s could not be found", providerAddr.ToSdkConsAddr()) } power := k.stakingKeeper.GetLastValidatorPower(ctx, validator.GetOperator()) - minPowerToOptIn, err := k.ComputeMinPowerToOptIn(ctx, k.GetLastBondedValidators(ctx), topN) - if err != nil { - k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err) + minPowerInTopN, found := k.GetMinimumPowerInTopN(ctx, chainID) + if !found { return errorsmod.Wrapf( - types.ErrCannotOptOutFromTopN, - "validator with power (%d) cannot opt out from Top N chain (%s) because the min power"+ - " could not be computed: %s", power, chainID, err.Error()) - + types.ErrUnknownConsumerChainId, + "Could not find minimum power in top N for chain with id: %s", chainID) } - if power >= minPowerToOptIn { + if power >= minPowerInTopN { return errorsmod.Wrapf( types.ErrCannotOptOutFromTopN, "validator with power (%d) cannot opt out from Top N chain (%s) because all validators"+ - " with at least %d power have to validate", power, chainID, minPowerToOptIn) + " with at least %d power have to validate", power, chainID, minPowerInTopN) } } @@ -104,9 +101,9 @@ func (k Keeper) OptInTopNValidators(ctx sdk.Context, chainID string, bondedValid } } -// ComputeMinPowerToOptIn returns the minimum power needed for a validator (from the bonded validators) -// to belong to the `topN` validators for a Top N chain. -func (k Keeper) ComputeMinPowerToOptIn(ctx sdk.Context, bondedValidators []stakingtypes.Validator, topN uint32) (int64, error) { +// ComputeMinPowerInTopN returns the minimum power needed for a validator (from the bonded validators) +// to belong to the `topN`% of validators for a Top N chain. +func (k Keeper) ComputeMinPowerInTopN(ctx sdk.Context, bondedValidators []stakingtypes.Validator, topN uint32) (int64, error) { if topN == 0 || topN > 100 { // Note that Top N chains have a lower limit on `topN`, namely that topN cannot be less than 50. // However, we can envision that this method could be used for other (future) reasons where this might not diff --git a/x/ccv/provider/keeper/partial_set_security_test.go b/x/ccv/provider/keeper/partial_set_security_test.go index 4209f7f6ba..bc0396d428 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -131,6 +131,11 @@ func TestHandleOptOutFromTopNChain(t *testing.T) { testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 4, []stakingtypes.Validator{valA, valB, valC, valD}, []int64{1, 2, 3, 4}, -1) // -1 to allow mocks AnyTimes + // initialize the minPowerInTopN correctly + minPowerInTopN, err := providerKeeper.ComputeMinPowerInTopN(ctx, []stakingtypes.Validator{valA, valB, valC, valD}, 50) + require.NoError(t, err) + providerKeeper.SetMinimumPowerInTopN(ctx, chainID, minPowerInTopN) + // opt in all validators providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(valAConsAddr)) providerKeeper.SetOptedIn(ctx, chainID, types.NewProviderConsAddress(valBConsAddr)) @@ -278,7 +283,7 @@ func TestOptInTopNValidators(t *testing.T) { require.Empty(t, providerKeeper.GetAllOptedIn(ctx, "chainID")) } -func TestComputeMinPowerToOptIn(t *testing.T) { +func TestComputeMinPowerInTopN(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() @@ -299,50 +304,50 @@ func TestComputeMinPowerToOptIn(t *testing.T) { createStakingValidator(ctx, mocks, 5, 6), } - m, err := providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 100) + m, err := providerKeeper.ComputeMinPowerInTopN(ctx, bondedValidators, 100) require.NoError(t, err) require.Equal(t, int64(1), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 97) + m, err = providerKeeper.ComputeMinPowerInTopN(ctx, bondedValidators, 97) require.NoError(t, err) require.Equal(t, int64(1), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 96) + m, err = providerKeeper.ComputeMinPowerInTopN(ctx, bondedValidators, 96) require.NoError(t, err) require.Equal(t, int64(3), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 85) + m, err = providerKeeper.ComputeMinPowerInTopN(ctx, bondedValidators, 85) require.NoError(t, err) require.Equal(t, int64(3), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 84) + m, err = providerKeeper.ComputeMinPowerInTopN(ctx, bondedValidators, 84) require.NoError(t, err) require.Equal(t, int64(5), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 65) + m, err = providerKeeper.ComputeMinPowerInTopN(ctx, bondedValidators, 65) require.NoError(t, err) require.Equal(t, int64(5), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 64) + m, err = providerKeeper.ComputeMinPowerInTopN(ctx, bondedValidators, 64) require.NoError(t, err) require.Equal(t, int64(6), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 50) + m, err = providerKeeper.ComputeMinPowerInTopN(ctx, bondedValidators, 50) require.NoError(t, err) require.Equal(t, int64(6), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 40) + m, err = providerKeeper.ComputeMinPowerInTopN(ctx, bondedValidators, 40) require.NoError(t, err) require.Equal(t, int64(10), m) - m, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 1) + m, err = providerKeeper.ComputeMinPowerInTopN(ctx, bondedValidators, 1) require.NoError(t, err) require.Equal(t, int64(10), m) - _, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 0) + _, err = providerKeeper.ComputeMinPowerInTopN(ctx, bondedValidators, 0) require.Error(t, err) - _, err = providerKeeper.ComputeMinPowerToOptIn(ctx, bondedValidators, 101) + _, err = providerKeeper.ComputeMinPowerInTopN(ctx, bondedValidators, 101) require.Error(t, err) } diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index bbb1858fa4..b394e38515 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -174,6 +174,28 @@ func (k Keeper) HandleConsumerModificationProposal(ctx sdk.Context, p *types.Con k.SetDenylist(ctx, p.ChainId, types.NewProviderConsAddress(consAddr)) } + oldTopN, found := k.GetTopN(ctx, p.ChainId) + if !found { + oldTopN = 0 + k.Logger(ctx).Info("consumer chain top N not found, treating as 0", "chainID", p.ChainId) + } + + // if the top N changes, we need to update the new minimum power in top N + if p.Top_N != oldTopN { + if p.Top_N > 0 { + // if the chain receives a non-zero top N value, store the minimum power in the top N + bondedValidators := k.GetLastBondedValidators(ctx) + minPower, err := k.ComputeMinPowerInTopN(ctx, bondedValidators, p.Top_N) + if err != nil { + return err + } + k.SetMinimumPowerInTopN(ctx, p.ChainId, minPower) + } else { + // if the chain receives a zero top N value, we delete the min power + k.DeleteMinimumPowerInTopN(ctx, p.ChainId) + } + } + return nil } @@ -196,6 +218,7 @@ func (k Keeper) StopConsumerChain(ctx sdk.Context, chainID string, closeChan boo k.DeleteInitTimeoutTimestamp(ctx, chainID) // Note: this call panics if the key assignment state is invalid k.DeleteKeyAssignments(ctx, chainID) + k.DeleteMinimumPowerInTopN(ctx, chainID) // close channel and delete the mappings between chain ID and channel ID if channelID, found := k.GetChainToChannel(ctx, chainID); found { @@ -295,16 +318,15 @@ func (k Keeper) MakeConsumerGenesis( // get the bonded validators from the staking module bondedValidators := k.GetLastBondedValidators(ctx) - if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { + if prop.Top_N > 0 { // in a Top-N chain, we automatically opt in all validators that belong to the top N - minPower, err := k.ComputeMinPowerToOptIn(ctx, bondedValidators, prop.Top_N) - if err == nil { - k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) - } else { + minPower, err := k.ComputeMinPowerInTopN(ctx, bondedValidators, prop.Top_N) + if err != nil { return gen, nil, err } + k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) + k.SetMinimumPowerInTopN(ctx, chainID, minPower) } - nextValidators := k.ComputeNextValidators(ctx, chainID, bondedValidators) k.SetConsumerValSet(ctx, chainID, nextValidators) diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index f9ec094a70..4749a21a23 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -226,8 +226,11 @@ func (k Keeper) QueueVSCPackets(ctx sdk.Context) { if topN > 0 { // in a Top-N chain, we automatically opt in all validators that belong to the top N - minPower, err := k.ComputeMinPowerToOptIn(ctx, bondedValidators, topN) + minPower, err := k.ComputeMinPowerInTopN(ctx, bondedValidators, topN) if err == nil { + // set the minimal power of validators in the top N in the store + k.SetMinimumPowerInTopN(ctx, chainID, minPower) + k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) } else { // we just log here and do not panic because panic-ing would halt the provider chain diff --git a/x/ccv/provider/migrations/migrator.go b/x/ccv/provider/migrations/migrator.go index c8b464a7a3..abc23c42d0 100644 --- a/x/ccv/provider/migrations/migrator.go +++ b/x/ccv/provider/migrations/migrator.go @@ -8,6 +8,7 @@ import ( v3 "github.com/cosmos/interchain-security/v4/x/ccv/provider/migrations/v3" v4 "github.com/cosmos/interchain-security/v4/x/ccv/provider/migrations/v4" v5 "github.com/cosmos/interchain-security/v4/x/ccv/provider/migrations/v5" + v6 "github.com/cosmos/interchain-security/v4/x/ccv/provider/migrations/v6" ) // Migrator is a struct for handling in-place store migrations. @@ -41,9 +42,16 @@ func (m Migrator) Migrate3to4(ctx sdktypes.Context) error { return nil } -// MigrateXtoY migrates x/ccvprovider state from consensus version 4 to 5. +// Migrate4to5 migrates x/ccvprovider state from consensus version 4 to 5. // The migration consists of setting a top N of 95 for all registered consumer chains. func (m Migrator) Migrate4to5(ctx sdktypes.Context) error { v5.MigrateTopNForRegisteredChains(ctx, m.providerKeeper) return nil } + +// Migrate5to6 migrates x/ccvprovider state from consensus version 5 to 6. +// The migration consists of computing and storing the minimal power in the top N for all registered consumer chains. +func (m Migrator) Migrate5to6(ctx sdktypes.Context) error { + v6.MigrateMinPowerInTopN(ctx, m.providerKeeper) + return nil +} diff --git a/x/ccv/provider/migrations/v6/migrations.go b/x/ccv/provider/migrations/v6/migrations.go new file mode 100644 index 0000000000..7e659de143 --- /dev/null +++ b/x/ccv/provider/migrations/v6/migrations.go @@ -0,0 +1,33 @@ +package v6 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" +) + +func MigrateMinPowerInTopN(ctx sdk.Context, providerKeeper providerkeeper.Keeper) { + // we only get the registered consumer chains and not also the proposed consumer chains because + // the minimal power is first set when the consumer chain addition proposal passes + registeredConsumerChains := providerKeeper.GetAllRegisteredConsumerChainIDs(ctx) + + for _, chain := range registeredConsumerChains { + // get the top N + topN, found := providerKeeper.GetTopN(ctx, chain) + if !found { + providerKeeper.Logger(ctx).Error("failed to get top N", "chain", chain) + continue + } else if topN == 0 { + providerKeeper.Logger(ctx).Info("top N is 0, not setting minimal power", "chain", chain) + } else { + // set the minimal power in the top N + bondedValidators := providerKeeper.GetLastBondedValidators(ctx) + minPower, err := providerKeeper.ComputeMinPowerInTopN(ctx, bondedValidators, topN) + if err != nil { + providerKeeper.Logger(ctx).Error("failed to compute min power in top N", "chain", chain, "topN", topN, "error", err) + continue + } + providerKeeper.SetMinimumPowerInTopN(ctx, chain, minPower) + } + } +} diff --git a/x/ccv/provider/module.go b/x/ccv/provider/module.go index 6b616f0de8..23c773e97a 100644 --- a/x/ccv/provider/module.go +++ b/x/ccv/provider/module.go @@ -140,7 +140,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw } // ConsensusVersion implements AppModule/ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 5 } +func (AppModule) ConsensusVersion() uint64 { return 6 } // BeginBlock implements the AppModule interface func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { diff --git a/x/ccv/provider/types/keys.go b/x/ccv/provider/types/keys.go index 14521b9289..ee11633e5c 100644 --- a/x/ccv/provider/types/keys.go +++ b/x/ccv/provider/types/keys.go @@ -183,6 +183,10 @@ const ( // per validator per consumer chain ConsumerCommissionRatePrefix + // MinimumPowerInTopNBytePrefix is the byte prefix for storing the + // minimum power required to be in the top N per consumer chain. + MinimumPowerInTopNBytePrefix + // NOTE: DO NOT ADD NEW BYTE PREFIXES HERE WITHOUT ADDING THEM TO getAllKeyPrefixes() IN keys_test.go ) @@ -601,6 +605,10 @@ func ConsumerCommissionRateKey(chainID string, providerAddr ProviderConsAddress) ) } +func MinimumPowerInTopNKey(chainID string) []byte { + return ChainIdWithLenKey(MinimumPowerInTopNBytePrefix, chainID) +} + // // End of generic helpers section // diff --git a/x/ccv/provider/types/keys_test.go b/x/ccv/provider/types/keys_test.go index eac11a0993..a0c14c4ebd 100644 --- a/x/ccv/provider/types/keys_test.go +++ b/x/ccv/provider/types/keys_test.go @@ -61,6 +61,7 @@ func getAllKeyPrefixes() []byte { providertypes.TopNBytePrefix, providertypes.ConsumerRewardsAllocationBytePrefix, providertypes.ConsumerCommissionRatePrefix, + providertypes.MinimumPowerInTopNBytePrefix, } } From d3ea99ba510f293f7cf1a18981d4a70fce7e90f0 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt <57488781+p-offtermatt@users.noreply.github.com> Date: Wed, 12 Jun 2024 18:04:35 +0200 Subject: [PATCH 066/102] fix: print test name instead of config name (#1961) * Fix: print test name instead of config name * Add the config back in the report --- tests/e2e/main.go | 11 +++++++---- tests/e2e/test_runner.go | 20 ++++++++++++-------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/tests/e2e/main.go b/tests/e2e/main.go index 5a1ee681e0..2cf0f33a6f 100644 --- a/tests/e2e/main.go +++ b/tests/e2e/main.go @@ -277,7 +277,7 @@ func parseArguments() (err error) { type testStepsWithConfig struct { config TestConfigType - steps []Step + steps StepChoice } func getTestCases(selectedPredefinedTests, selectedTestFiles TestSet, providerVersions, @@ -316,13 +316,12 @@ func getTestCases(selectedPredefinedTests, selectedTestFiles TestSet, providerVe log.Fatalf("Step choice '%s' not found.\nsee usage info:\n%s", tc, getTestCaseUsageString()) } - testSteps := stepChoices[tc].steps if testConfig == "" { testConfig = stepChoices[tc].testConfig } tests = append(tests, testStepsWithConfig{ config: testConfig, - steps: testSteps, + steps: stepChoices[tc], }, ) } @@ -351,7 +350,11 @@ func getTestCases(selectedPredefinedTests, selectedTestFiles TestSet, providerVe tests = append(tests, testStepsWithConfig{ config: testConfig, - steps: testCase, + steps: StepChoice{ + name: testFileName, + steps: testCase, + description: fmt.Sprintf("Steps from file %s", testFileName), + }, }) } diff --git a/tests/e2e/test_runner.go b/tests/e2e/test_runner.go index 7c02dd6d2d..c3ced5ab61 100644 --- a/tests/e2e/test_runner.go +++ b/tests/e2e/test_runner.go @@ -19,7 +19,7 @@ const ( // It sets up the test environment and the test driver to run the tests type TestRunner struct { config TestConfig - steps []Step + stepChoice StepChoice testDriver TestCaseDriver target ExecutionTarget verbose bool @@ -86,7 +86,7 @@ func (tr *TestRunner) Run() error { } tr.testDriver = GetTestCaseDriver(tr.config) - err = tr.testDriver.Run(tr.steps, tr.target, tr.verbose) + err = tr.testDriver.Run(tr.stepChoice.steps, tr.target, tr.verbose) if err != nil { tr.result.Failed() // not tearing down environment for troubleshooting reasons on container @@ -118,13 +118,13 @@ func (tr *TestRunner) Setup(testCfg TestConfig) error { return nil } -func CreateTestRunner(config TestConfig, steps []Step, target ExecutionTarget, verbose bool) TestRunner { +func CreateTestRunner(config TestConfig, stepChoice StepChoice, target ExecutionTarget, verbose bool) TestRunner { return TestRunner{ - target: target, - steps: steps, - config: config, - verbose: verbose, - result: TestResult{Status: TEST_STATUS_NOTRUN}, + target: target, + stepChoice: stepChoice, + config: config, + verbose: verbose, + result: TestResult{Status: TEST_STATUS_NOTRUN}, } } @@ -133,8 +133,10 @@ func (tr *TestRunner) Info() string { return fmt.Sprintf(` ------------------------------------------ Test name : %s +Config: %s Target: %s ------------------------------------------`, + tr.stepChoice.name, tr.config.name, tr.target.Info(), ) @@ -144,12 +146,14 @@ func (tr *TestRunner) Report() string { return fmt.Sprintf(` ------------------------------------------ Test name : %s +Config: %s Target: %s - Status: %s - Result: %s - Duration: %s - StartTime: %s ------------------------------------------`, + tr.stepChoice.name, tr.config.name, tr.target.Info(), tr.result.Status, From c454cc7aea5d53c3f32548c26e19cbbcd1c1d06c Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Thu, 13 Jun 2024 10:59:30 +0200 Subject: [PATCH 067/102] chore: add ICS `release/v5.1.x` branch to Mergify (#1962) add release/v5.1.x brancg to mergify --- .github/dependabot.yml | 10 ++++++++++ .mergify.yml | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 586ef97a95..cb2c06d1a9 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -48,3 +48,13 @@ updates: labels: - dependencies + - package-ecosystem: gomod + directory: "/" + schedule: + interval: daily + target-branch: "release/v5.1.x" + # Only allow automated security-related dependency updates on release branches. + open-pull-requests-limit: 0 + labels: + - dependencies + diff --git a/.mergify.yml b/.mergify.yml index 18697f0bf4..6344fe199a 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -34,3 +34,11 @@ pull_request_rules: backport: branches: - release/v5.x + - name: Backport patches to the release/v5.1.x branch + conditions: + - base=main + - label=A:backport/v5.1.x + actions: + backport: + branches: + - release/v5.1.x From 5862956b930b687216f798b5b5677fd9850a6218 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:11:43 +0200 Subject: [PATCH 068/102] build(deps): bump google.golang.org/protobuf from 1.34.1 to 1.34.2 (#1958) Bumps google.golang.org/protobuf from 1.34.1 to 1.34.2. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5b46550220..4ec104c1fa 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( golang.org/x/sys v0.18.0 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/grpc v1.64.0 - google.golang.org/protobuf v1.34.1 + google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 6640cdf97a..cfa6b7926d 100644 --- a/go.sum +++ b/go.sum @@ -1628,8 +1628,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ 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.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +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= From 97e6599e990f13e8150751e0ba315a61e71c0c16 Mon Sep 17 00:00:00 2001 From: insumity Date: Wed, 19 Jun 2024 17:56:29 +0200 Subject: [PATCH 069/102] feat!: only distribute rewards to validators that have been validating a consumer chain for some time (#1929) * init commit * added a warning * took into account comments * init commit * added a warning * took into account comments * added a comment * Update .changelog/unreleased/improvements/provider/1929-distribute-rewards-to-long-term-validating-validators.md Co-authored-by: Marius Poke * Update .changelog/unreleased/state-breaking/provider/1929-distribute-rewards-to-long-term-validating-validators.md Co-authored-by: Marius Poke * took into account comments --------- Co-authored-by: Marius Poke --- ...ards-to-long-term-validating-validators.md | 3 + ...ards-to-long-term-validating-validators.md | 3 + docs/docs/validators/withdraw_rewards.md | 11 +- .../ccv/provider/v1/provider.proto | 8 + tests/integration/distribution.go | 135 +++++++- x/ccv/provider/keeper/distribution.go | 20 ++ x/ccv/provider/keeper/distribution_test.go | 26 ++ x/ccv/provider/keeper/params.go | 9 + x/ccv/provider/keeper/params_test.go | 1 + x/ccv/provider/keeper/proposal_test.go | 3 +- x/ccv/provider/keeper/relay_test.go | 49 +++ x/ccv/provider/keeper/validator_set_update.go | 25 ++ x/ccv/provider/migrations/migrator.go | 4 +- .../provider/migrations/v6/migration_test.go | 27 ++ x/ccv/provider/migrations/v6/migrations.go | 10 + x/ccv/provider/module.go | 4 + x/ccv/provider/types/genesis_test.go | 26 +- x/ccv/provider/types/params.go | 55 ++- x/ccv/provider/types/params_test.go | 28 +- x/ccv/provider/types/provider.pb.go | 327 +++++++++++------- 20 files changed, 595 insertions(+), 179 deletions(-) create mode 100644 .changelog/unreleased/improvements/provider/1929-distribute-rewards-to-long-term-validating-validators.md create mode 100644 .changelog/unreleased/state-breaking/provider/1929-distribute-rewards-to-long-term-validating-validators.md create mode 100644 x/ccv/provider/migrations/v6/migration_test.go diff --git a/.changelog/unreleased/improvements/provider/1929-distribute-rewards-to-long-term-validating-validators.md b/.changelog/unreleased/improvements/provider/1929-distribute-rewards-to-long-term-validating-validators.md new file mode 100644 index 0000000000..b4ff9d6341 --- /dev/null +++ b/.changelog/unreleased/improvements/provider/1929-distribute-rewards-to-long-term-validating-validators.md @@ -0,0 +1,3 @@ +- Only start distributing rewards to validators after they have been validating + for a fixed number of blocks. Introduces the `NumberOfEpochsToStartReceivingRewards` param. + ([\#1929](https://github.com/cosmos/interchain-security/pull/1929)) diff --git a/.changelog/unreleased/state-breaking/provider/1929-distribute-rewards-to-long-term-validating-validators.md b/.changelog/unreleased/state-breaking/provider/1929-distribute-rewards-to-long-term-validating-validators.md new file mode 100644 index 0000000000..b4ff9d6341 --- /dev/null +++ b/.changelog/unreleased/state-breaking/provider/1929-distribute-rewards-to-long-term-validating-validators.md @@ -0,0 +1,3 @@ +- Only start distributing rewards to validators after they have been validating + for a fixed number of blocks. Introduces the `NumberOfEpochsToStartReceivingRewards` param. + ([\#1929](https://github.com/cosmos/interchain-security/pull/1929)) diff --git a/docs/docs/validators/withdraw_rewards.md b/docs/docs/validators/withdraw_rewards.md index 1a9ab1fb3c..15d27b3006 100644 --- a/docs/docs/validators/withdraw_rewards.md +++ b/docs/docs/validators/withdraw_rewards.md @@ -2,8 +2,17 @@ sidebar_position: 3 --- -# Withdrawing consumer chain validator rewards +# Consumer chain validator rewards +:::warning +A validator can only receive rewards from a consumer chain if the validator has been validating the consumer chain +for some time. Specifically, the validator has to be a consumer validator of the consumer chain for at least +`NumberOfEpochsToStartReceivingRewards * BlocksPerEpoch` blocks (run `interchain-security-pd query provider params` for +the actual values of the `NumberOfEpochsToStartReceivingRewards` and `BlocksPerEpoch` params). +::: + + +## Withdrawing rewards Here are example steps for withdrawing rewards from consumer chains in the provider chain :::info diff --git a/proto/interchain_security/ccv/provider/v1/provider.proto b/proto/interchain_security/ccv/provider/v1/provider.proto index 2a25eb533f..e31b3a150f 100644 --- a/proto/interchain_security/ccv/provider/v1/provider.proto +++ b/proto/interchain_security/ccv/provider/v1/provider.proto @@ -243,6 +243,9 @@ message Params { // The number of blocks that comprise an epoch. int64 blocks_per_epoch = 10; + + // The number of epochs a validator has to validate a consumer chain in order to start receiving rewards from that chain. + int64 number_of_epochs_to_start_receiving_rewards = 11; } // SlashAcks contains cons addresses of consumer chain validators @@ -360,6 +363,11 @@ message ConsumerValidator { int64 power = 2; // public key the validator uses on the consumer chain during this epoch tendermint.crypto.PublicKey consumer_public_key = 3; + // height the validator had when it FIRST became a consumer validator + // If a validator becomes a consumer validator at height `H` and is continuously a consumer validator for all the upcoming + // epochs, then the height of the validator SHOULD remain `H`. This height only resets to a different height if a validator + // stops being a consumer validator during an epoch and later becomes again a consumer validator. + int64 join_height = 4; } // ConsumerRewardsAllocation stores the rewards allocated by a consumer chain // to the consumer rewards pool. It is used to allocate the tokens to the consumer diff --git a/tests/integration/distribution.go b/tests/integration/distribution.go index 8229850a63..2bea909c75 100644 --- a/tests/integration/distribution.go +++ b/tests/integration/distribution.go @@ -705,9 +705,14 @@ func (s *CCVTestSuite) TestAllocateTokens() { totalRewards := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))} + // increase the block height so validators are eligible for consumer rewards (see `IsEligibleForConsumerRewards`) + numberOfBlocksToStartReceivingRewards := + providerKeeper.GetNumberOfEpochsToStartReceivingRewards(s.providerCtx()) * providerKeeper.GetBlocksPerEpoch(s.providerCtx()) + providerCtx := s.providerCtx().WithBlockHeight(numberOfBlocksToStartReceivingRewards + s.providerCtx().BlockHeight()) + // fund consumer rewards pool bankKeeper.SendCoinsFromAccountToModule( - s.providerCtx(), + providerCtx, s.providerChain.SenderAccount.GetAddress(), providertypes.ConsumerRewardsPool, totalRewards, @@ -718,7 +723,7 @@ func (s *CCVTestSuite) TestAllocateTokens() { for chainID := range s.consumerBundles { // update consumer allocation providerKeeper.SetConsumerRewardsAllocation( - s.providerCtx(), + providerCtx, chainID, providertypes.ConsumerRewardsAllocation{ Rewards: sdk.NewDecCoinsFromCoins(rewardsPerConsumer...), @@ -738,16 +743,16 @@ func (s *CCVTestSuite) TestAllocateTokens() { }, ) - valRewards := distributionKeeper.GetValidatorOutstandingRewards(s.providerCtx(), sdk.ValAddress(val.Address)) + valRewards := distributionKeeper.GetValidatorOutstandingRewards(providerCtx, sdk.ValAddress(val.Address)) lastValOutRewards[sdk.ValAddress(val.Address).String()] = valRewards.Rewards } // store community pool balance - lastCommPool := distributionKeeper.GetFeePoolCommunityCoins(s.providerCtx()) + lastCommPool := distributionKeeper.GetFeePoolCommunityCoins(providerCtx) // execute BeginBlock to trigger the token allocation providerKeeper.BeginBlockRD( - s.providerCtx(), + providerCtx, abci.RequestBeginBlock{ LastCommitInfo: abci.CommitInfo{ Votes: votes, @@ -760,7 +765,7 @@ func (s *CCVTestSuite) TestAllocateTokens() { // compute the expected validators token allocation by subtracting the community tax rewardsPerConsumerDec := sdk.NewDecCoinsFromCoins(rewardsPerConsumer...) - communityTax := distributionKeeper.GetCommunityTax(s.providerCtx()) + communityTax := distributionKeeper.GetCommunityTax(providerCtx) validatorsExpRewards := rewardsPerConsumerDec. MulDecTruncate(math.LegacyOneDec().Sub(communityTax)). // multiply by the number of consumers since all the validators opted in @@ -770,7 +775,7 @@ func (s *CCVTestSuite) TestAllocateTokens() { // verify the validator tokens allocation // note that the validators have the same voting power to keep things simple for _, val := range s.providerChain.Vals.Validators { - valRewards := distributionKeeper.GetValidatorOutstandingRewards(s.providerCtx(), sdk.ValAddress(val.Address)) + valRewards := distributionKeeper.GetValidatorOutstandingRewards(providerCtx, sdk.ValAddress(val.Address)) s.Require().Equal( valRewards.Rewards, lastValOutRewards[sdk.ValAddress(val.Address).String()].Add(perValExpReward...), @@ -778,7 +783,7 @@ func (s *CCVTestSuite) TestAllocateTokens() { } commPoolExpRewards := sdk.NewDecCoinsFromCoins(totalRewards...).Sub(validatorsExpRewards) - currCommPool := distributionKeeper.GetFeePoolCommunityCoins(s.providerCtx()) + currCommPool := distributionKeeper.GetFeePoolCommunityCoins(providerCtx) s.Require().Equal(currCommPool, (lastCommPool.Add(commPoolExpRewards...))) } @@ -935,7 +940,7 @@ func (s *CCVTestSuite) prepareRewardDist() { s.coordinator.CommitNBlocks(s.consumerChain, uint64(blocksToGo)) } -func (s *CCVTestSuite) TestAllocateTokensToValidator() { +func (s *CCVTestSuite) TestAllocateTokensToConsumerValidators() { providerKeeper := s.providerApp.GetProviderKeeper() distributionKeeper := s.providerApp.GetTestDistributionKeeper() bankKeeper := s.providerApp.GetTestBankKeeper() @@ -981,6 +986,10 @@ func (s *CCVTestSuite) TestAllocateTokensToValidator() { s.Run(tc.name, func() { ctx, _ := s.providerCtx().CacheContext() + // increase the block height so validators are eligible for consumer rewards (see `IsEligibleForConsumerRewards`) + ctx = ctx.WithBlockHeight(providerKeeper.GetNumberOfEpochsToStartReceivingRewards(ctx)*providerKeeper.GetBlocksPerEpoch(ctx) + + ctx.BlockHeight()) + // change the consumer valset consuVals := providerKeeper.GetConsumerValSet(ctx, chainID) providerKeeper.DeleteConsumerValSet(ctx, chainID) @@ -1061,6 +1070,114 @@ func (s *CCVTestSuite) TestAllocateTokensToValidator() { } } +// TestAllocateTokensToConsumerValidatorsWithDifferentValidatorHeights tests `AllocateTokensToConsumerValidators` with +// consumer validators that have different heights. Specifically, test that validators that have been consumer validators +// for some time receive rewards, while validators that recently became consumer validators do not receive rewards. +func (s *CCVTestSuite) TestAllocateTokensToConsumerValidatorsWithDifferentValidatorHeights() { + // Note this test is an adaptation of a `TestAllocateTokensToConsumerValidators` testcase. + providerKeeper := s.providerApp.GetProviderKeeper() + distributionKeeper := s.providerApp.GetTestDistributionKeeper() + bankKeeper := s.providerApp.GetTestBankKeeper() + + chainID := s.consumerChain.ChainID + + tokens := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyNewDecFromIntWithPrec(math.NewInt(999), 2))} + rate := sdk.OneDec() + expAllocated := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyNewDecFromIntWithPrec(math.NewInt(999), 2))} + + ctx, _ := s.providerCtx().CacheContext() + // If the provider chain has not yet reached `GetNumberOfEpochsToStartReceivingRewards * GetBlocksPerEpoch` block height, + // then all validators receive rewards (see `IsEligibleForConsumerRewards`). In this test, we want to check whether + // validators receive rewards or not based on how long they have been consumer validators. Because of this, we increase the block height. + ctx = ctx.WithBlockHeight(providerKeeper.GetNumberOfEpochsToStartReceivingRewards(ctx)*providerKeeper.GetBlocksPerEpoch(ctx) + 1) + + // update the consumer validators + consuVals := providerKeeper.GetConsumerValSet(ctx, chainID) + // first 2 validators were consumer validators since block height 1 and hence get rewards + consuVals[0].JoinHeight = 1 + consuVals[1].JoinHeight = 1 + // last 2 validators were consumer validators since block height 2 and hence do not get rewards because they + // have not been consumer validators for `GetNumberOfEpochsToStartReceivingRewards * GetBlocksPerEpoch` blocks + consuVals[2].JoinHeight = 2 + consuVals[3].JoinHeight = 2 + providerKeeper.SetConsumerValSet(ctx, chainID, consuVals) + + providerKeeper.DeleteConsumerValSet(ctx, chainID) + providerKeeper.SetConsumerValSet(ctx, chainID, consuVals) + consuVals = providerKeeper.GetConsumerValSet(ctx, chainID) + + // set the same consumer commission rate for all consumer validators + for _, v := range consuVals { + provAddr := providertypes.NewProviderConsAddress(sdk.ConsAddress(v.ProviderConsAddr)) + err := providerKeeper.SetConsumerCommissionRate( + ctx, + chainID, + provAddr, + rate, + ) + s.Require().NoError(err) + } + + // allocate tokens + res := providerKeeper.AllocateTokensToConsumerValidators( + ctx, + chainID, + tokens, + ) + + // check that the expected result is returned + s.Require().Equal(expAllocated, res) + + // rewards are expected to be allocated evenly between validators 3 and 4 + rewardsPerVal := expAllocated.QuoDec(sdk.NewDec(int64(2))) + + // assert that the rewards are allocated to the first 2 validators + for _, v := range consuVals[0:2] { + valAddr := sdk.ValAddress(v.ProviderConsAddr) + rewards := s.providerApp.GetTestDistributionKeeper().GetValidatorOutstandingRewards( + ctx, + valAddr, + ) + s.Require().Equal(rewardsPerVal, rewards.Rewards) + + // send rewards to the distribution module + valRewardsTrunc, _ := rewards.Rewards.TruncateDecimal() + err := bankKeeper.SendCoinsFromAccountToModule( + ctx, + s.providerChain.SenderAccount.GetAddress(), + distrtypes.ModuleName, + valRewardsTrunc) + s.Require().NoError(err) + + // check that validators can withdraw their rewards + withdrawnCoins, err := distributionKeeper.WithdrawValidatorCommission( + ctx, + valAddr, + ) + s.Require().NoError(err) + + // check that the withdrawn coins is equal to the entire reward amount + // times the set consumer commission rate + commission := rewards.Rewards.MulDec(rate) + c, _ := commission.TruncateDecimal() + s.Require().Equal(withdrawnCoins, c) + + // check that validators get rewards in their balance + s.Require().Equal(withdrawnCoins, bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddr))) + } + + // assert that no rewards are allocated to the last 2 validators because they have not been consumer validators + // for at least `GetNumberOfEpochsToStartReceivingRewards * GetBlocksPerEpoch` blocks + for _, v := range consuVals[2:4] { + valAddr := sdk.ValAddress(v.ProviderConsAddr) + rewards := s.providerApp.GetTestDistributionKeeper().GetValidatorOutstandingRewards( + ctx, + valAddr, + ) + s.Require().Zero(rewards.Rewards) + } +} + // TestMultiConsumerRewardsDistribution tests the rewards distribution of multiple consumers chains func (s *CCVTestSuite) TestMultiConsumerRewardsDistribution() { s.SetupAllCCVChannels() diff --git a/x/ccv/provider/keeper/distribution.go b/x/ccv/provider/keeper/distribution.go index 7d875b7c38..2259e472d1 100644 --- a/x/ccv/provider/keeper/distribution.go +++ b/x/ccv/provider/keeper/distribution.go @@ -127,6 +127,15 @@ func (k Keeper) AllocateTokens(ctx sdk.Context) { } } +// IsEligibleForConsumerRewards returns `true` if the validator with `consumerValidatorHeight` has been a consumer +// validator for a long period of time and hence is eligible to receive rewards, and false otherwise +func (k Keeper) IsEligibleForConsumerRewards(ctx sdk.Context, consumerValidatorHeight int64) bool { + numberOfBlocksToStartReceivingRewards := k.GetNumberOfEpochsToStartReceivingRewards(ctx) * k.GetBlocksPerEpoch(ctx) + + // a validator is eligible for rewards if it has been a consumer validator for `NumberOfEpochsToStartReceivingRewards` epochs + return (ctx.BlockHeight() - consumerValidatorHeight) >= numberOfBlocksToStartReceivingRewards +} + // AllocateTokensToConsumerValidators allocates tokens // to the given consumer chain's validator set func (k Keeper) AllocateTokensToConsumerValidators( @@ -147,6 +156,11 @@ func (k Keeper) AllocateTokensToConsumerValidators( // Allocate tokens by iterating over the consumer validators for _, consumerVal := range k.GetConsumerValSet(ctx, chainID) { + // if a validator is not eligible, this means that the other eligible validators would get more rewards + if !k.IsEligibleForConsumerRewards(ctx, consumerVal.JoinHeight) { + continue + } + consAddr := sdk.ConsAddress(consumerVal.ProviderConsAddr) // get the validator tokens fraction using its voting power @@ -240,6 +254,12 @@ func (k Keeper) GetConsumerRewardsPool(ctx sdk.Context) sdk.Coins { func (k Keeper) ComputeConsumerTotalVotingPower(ctx sdk.Context, chainID string) (totalPower int64) { // sum the consumer validators set voting powers for _, v := range k.GetConsumerValSet(ctx, chainID) { + + // only consider the voting power of a validator that would receive rewards (i.e., validator has been validating for a number of blocks) + if !k.IsEligibleForConsumerRewards(ctx, v.JoinHeight) { + continue + } + totalPower += v.Power } diff --git a/x/ccv/provider/keeper/distribution_test.go b/x/ccv/provider/keeper/distribution_test.go index d95ae598c0..2c80b418b0 100644 --- a/x/ccv/provider/keeper/distribution_test.go +++ b/x/ccv/provider/keeper/distribution_test.go @@ -23,6 +23,14 @@ func TestComputeConsumerTotalVotingPower(t *testing.T) { keeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() + // `ComputeConsumerTotalVotingPower` used in this test retrieves the blocks per epoch, so we need to set this param + params := providertypes.DefaultParams() + params.BlocksPerEpoch = 1 + keeper.SetParams(ctx, params) + + // increase the block height so validators are eligible for consumer rewards (see `IsEligibleForConsumerRewards`) + ctx = ctx.WithBlockHeight(params.NumberOfEpochsToStartReceivingRewards * params.BlocksPerEpoch) + createVal := func(power int64) tmtypes.Validator { signer := tmtypes.NewMockPV() val := tmtypes.NewValidator(signer.PrivKey.PubKey(), power) @@ -270,3 +278,21 @@ func TestGetConsumerRewardsAllocationNil(t *testing.T) { } require.Equal(t, expectedRewardAllocation, alloc) } + +func TestIsEligibleForConsumerRewards(t *testing.T) { + keeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + params := providertypes.DefaultParams() + params.NumberOfEpochsToStartReceivingRewards = 10 + params.BlocksPerEpoch = 5 + keeper.SetParams(ctx, params) + + numberOfBlocks := params.NumberOfEpochsToStartReceivingRewards * params.BlocksPerEpoch + + require.False(t, keeper.IsEligibleForConsumerRewards(ctx.WithBlockHeight(numberOfBlocks-1), 0)) + require.True(t, keeper.IsEligibleForConsumerRewards(ctx.WithBlockHeight(numberOfBlocks), 0)) + require.True(t, keeper.IsEligibleForConsumerRewards(ctx.WithBlockHeight(numberOfBlocks+1), 0)) + require.True(t, keeper.IsEligibleForConsumerRewards(ctx.WithBlockHeight(numberOfBlocks+1), 1)) + require.False(t, keeper.IsEligibleForConsumerRewards(ctx.WithBlockHeight(numberOfBlocks+1), 2)) +} diff --git a/x/ccv/provider/keeper/params.go b/x/ccv/provider/keeper/params.go index f74baf656e..b5854e5b59 100644 --- a/x/ccv/provider/keeper/params.go +++ b/x/ccv/provider/keeper/params.go @@ -85,6 +85,14 @@ func (k Keeper) GetBlocksPerEpoch(ctx sdk.Context) int64 { return b } +// GetNumberOfEpochsToStartReceivingRewards returns the number of epochs needed by a validator to continuously validate +// to start receiving rewards +func (k Keeper) GetNumberOfEpochsToStartReceivingRewards(ctx sdk.Context) int64 { + var b int64 + k.paramSpace.Get(ctx, types.KeyNumberOfEpochsToStartReceivingRewards, &b) + return b +} + // GetParams returns the paramset for the provider module func (k Keeper) GetParams(ctx sdk.Context) types.Params { return types.NewParams( @@ -97,6 +105,7 @@ func (k Keeper) GetParams(ctx sdk.Context) types.Params { k.GetSlashMeterReplenishFraction(ctx), k.GetConsumerRewardDenomRegistrationFee(ctx), k.GetBlocksPerEpoch(ctx), + k.GetNumberOfEpochsToStartReceivingRewards(ctx), ) } diff --git a/x/ccv/provider/keeper/params_test.go b/x/ccv/provider/keeper/params_test.go index 88175431c0..9dd10076a6 100644 --- a/x/ccv/provider/keeper/params_test.go +++ b/x/ccv/provider/keeper/params_test.go @@ -49,6 +49,7 @@ func TestParams(t *testing.T) { Amount: sdk.NewInt(10000000), }, 600, + 24, ) providerKeeper.SetParams(ctx, newParams) params = providerKeeper.GetParams(ctx) diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index a7e26534b5..09942f82f1 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -839,7 +839,8 @@ func TestMakeConsumerGenesis(t *testing.T) { Denom: "stake", Amount: sdk.NewInt(1000000), }, - BlocksPerEpoch: 600, + BlocksPerEpoch: 600, + NumberOfEpochsToStartReceivingRewards: 24, } providerKeeper.SetParams(ctx, moduleParams) defer ctrl.Finish() diff --git a/x/ccv/provider/keeper/relay_test.go b/x/ccv/provider/keeper/relay_test.go index a4fe6e60ff..c71a53d13d 100644 --- a/x/ccv/provider/keeper/relay_test.go +++ b/x/ccv/provider/keeper/relay_test.go @@ -87,6 +87,55 @@ func TestQueueVSCPackets(t *testing.T) { } } +// TestQueueVSCPacketsDoesNotResetConsumerValidatorsHeights checks that the heights of consumer validators are not +// getting incorrectly updated +func TestQueueVSCPacketsDoesNotResetConsumerValidatorsHeights(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + chainHeight := int64(987654321) + ctx = ctx.WithBlockHeight(chainHeight) + providerKeeper.SetParams(ctx, providertypes.DefaultParams()) + + // mock 2 bonded validators + valA := createStakingValidator(ctx, mocks, 1, 1) + valAConsAddr, _ := valA.GetConsAddr() + valAPubKey, _ := valA.TmConsPublicKey() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valAConsAddr).Return(valA, true).AnyTimes() + valB := createStakingValidator(ctx, mocks, 2, 2) + valBConsAddr, _ := valB.GetConsAddr() + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valBConsAddr).Return(valB, true).AnyTimes() + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 2, []stakingtypes.Validator{valA, valB}, []int64{1, 2}, -1) + + // set a consumer client, so we have a consumer chain (i.e., `k.GetAllConsumerChains(ctx)` is non empty) + providerKeeper.SetConsumerClientId(ctx, "chainID", "clientID") + + // opt in validator A and set as a consumer validator + providerKeeper.SetOptedIn(ctx, "chainID", providertypes.NewProviderConsAddress(valAConsAddr)) + consumerValidatorA := types.ConsumerValidator{ + ProviderConsAddr: valAConsAddr, + Power: 1, + ConsumerPublicKey: &valAPubKey, + JoinHeight: 123456789, + } + providerKeeper.SetConsumerValidator(ctx, "chainID", consumerValidatorA) + + // Opt in validator B. Note that validator B is not a consumer validator and hence would become a consumer + // validator for the first time after the `QueueVSCPackets` call. + providerKeeper.SetOptedIn(ctx, "chainID", providertypes.NewProviderConsAddress(valBConsAddr)) + + providerKeeper.QueueVSCPackets(ctx) + + // the height of consumer validator A should not be modified because A was already a consumer validator + cv, _ := providerKeeper.GetConsumerValidator(ctx, "chainID", providertypes.NewProviderConsAddress(valAConsAddr)) + require.Equal(t, consumerValidatorA.JoinHeight, cv.JoinHeight, "the consumer validator's height was erroneously modified") + + // the height of consumer validator B is set to be the same as the one of the current chain height because this + // consumer validator becomes a consumer validator for the first time (i.e., was not a consumer validator in the previous epoch) + cv, _ = providerKeeper.GetConsumerValidator(ctx, "chainID", providertypes.NewProviderConsAddress(valBConsAddr)) + require.Equal(t, chainHeight, cv.JoinHeight, "the consumer validator's height was not correctly set") +} + // TestOnRecvVSCMaturedPacket tests the OnRecvVSCMaturedPacket method of the keeper. // // Note: Handling logic itself is not tested here. diff --git a/x/ccv/provider/keeper/validator_set_update.go b/x/ccv/provider/keeper/validator_set_update.go index 9ba2c961ab..de46e880ab 100644 --- a/x/ccv/provider/keeper/validator_set_update.go +++ b/x/ccv/provider/keeper/validator_set_update.go @@ -72,6 +72,23 @@ func (k Keeper) IsConsumerValidator(ctx sdk.Context, chainID string, providerAdd return store.Get(types.ConsumerValidatorKey(chainID, providerAddr.ToSdkConsAddr())) != nil } +// GetConsumerValidator returns the consumer validator with `providerAddr` if it exists for chain `chainID` +func (k Keeper) GetConsumerValidator(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress) (types.ConsumerValidator, bool) { + store := ctx.KVStore(k.storeKey) + marshalledConsumerValidator := store.Get(types.ConsumerValidatorKey(chainID, providerAddr.ToSdkConsAddr())) + + if marshalledConsumerValidator == nil { + return types.ConsumerValidator{}, false + } + + var validator types.ConsumerValidator + if err := validator.Unmarshal(marshalledConsumerValidator); err != nil { + panic(fmt.Errorf("failed to unmarshal ConsumerValidator: %w", err)) + } + + return validator, true +} + // GetConsumerValSet returns all the consumer validators for chain `chainID` func (k Keeper) GetConsumerValSet( ctx sdk.Context, @@ -152,10 +169,18 @@ func (k Keeper) CreateConsumerValidator(ctx sdk.Context, chainID string, validat } } + height := ctx.BlockHeight() + if v, found := k.GetConsumerValidator(ctx, chainID, types.ProviderConsAddress{Address: consAddr}); found { + // if validator was already a consumer validator, then do not update the height set the first time + // the validator became a consumer validator + height = v.JoinHeight + } + return types.ConsumerValidator{ ProviderConsAddr: consAddr, Power: power, ConsumerPublicKey: &consumerPublicKey, + JoinHeight: height, }, nil } diff --git a/x/ccv/provider/migrations/migrator.go b/x/ccv/provider/migrations/migrator.go index abc23c42d0..3ed41da6dd 100644 --- a/x/ccv/provider/migrations/migrator.go +++ b/x/ccv/provider/migrations/migrator.go @@ -50,8 +50,10 @@ func (m Migrator) Migrate4to5(ctx sdktypes.Context) error { } // Migrate5to6 migrates x/ccvprovider state from consensus version 5 to 6. -// The migration consists of computing and storing the minimal power in the top N for all registered consumer chains. +// The migration consists of setting the `NumberOfEpochsToStartReceivingRewards` param, as well as +// computing and storing the minimal power in the top N for all registered consumer chains. func (m Migrator) Migrate5to6(ctx sdktypes.Context) error { + v6.MigrateParams(ctx, m.paramSpace) v6.MigrateMinPowerInTopN(ctx, m.providerKeeper) return nil } diff --git a/x/ccv/provider/migrations/v6/migration_test.go b/x/ccv/provider/migrations/v6/migration_test.go new file mode 100644 index 0000000000..a5ae1b0d0c --- /dev/null +++ b/x/ccv/provider/migrations/v6/migration_test.go @@ -0,0 +1,27 @@ +package v6 + +import ( + "testing" + + "github.com/stretchr/testify/require" + + testutil "github.com/cosmos/interchain-security/v4/testutil/keeper" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" +) + +func TestMigrateParams(t *testing.T) { + inMemParams := testutil.NewInMemKeeperParams(t) + _, ctx, ctrl, _ := testutil.GetProviderKeeperAndCtx(t, inMemParams) + defer ctrl.Finish() + + // initially number of epochs param does not exist + require.False(t, inMemParams.ParamsSubspace.Has(ctx, providertypes.KeyNumberOfEpochsToStartReceivingRewards)) + + MigrateParams(ctx, *inMemParams.ParamsSubspace) + + // after migration, number of epochs epoch param should exist and be equal to default + require.True(t, inMemParams.ParamsSubspace.Has(ctx, providertypes.KeyNumberOfEpochsToStartReceivingRewards)) + var numberOfEpochsParam int64 + inMemParams.ParamsSubspace.Get(ctx, providertypes.KeyNumberOfEpochsToStartReceivingRewards, &numberOfEpochsParam) + require.Equal(t, providertypes.DefaultNumberOfEpochsToStartReceivingRewards, numberOfEpochsParam) +} diff --git a/x/ccv/provider/migrations/v6/migrations.go b/x/ccv/provider/migrations/v6/migrations.go index 7e659de143..9395d625e3 100644 --- a/x/ccv/provider/migrations/v6/migrations.go +++ b/x/ccv/provider/migrations/v6/migrations.go @@ -2,6 +2,8 @@ package v6 import ( sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" providerkeeper "github.com/cosmos/interchain-security/v4/x/ccv/provider/keeper" ) @@ -31,3 +33,11 @@ func MigrateMinPowerInTopN(ctx sdk.Context, providerKeeper providerkeeper.Keeper } } } + +// MigrateParams adds missing provider chain params to the param store. +func MigrateParams(ctx sdk.Context, paramsSubspace paramtypes.Subspace) { + if !paramsSubspace.HasKeyTable() { + paramsSubspace.WithKeyTable(providertypes.ParamKeyTable()) + } + paramsSubspace.Set(ctx, providertypes.KeyNumberOfEpochsToStartReceivingRewards, providertypes.DefaultNumberOfEpochsToStartReceivingRewards) +} diff --git a/x/ccv/provider/module.go b/x/ccv/provider/module.go index 23c773e97a..e78a1e051e 100644 --- a/x/ccv/provider/module.go +++ b/x/ccv/provider/module.go @@ -118,6 +118,10 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { if err := cfg.RegisterMigration(providertypes.ModuleName, 4, m.Migrate4to5); err != nil { panic(fmt.Sprintf("failed to register migrator for %s: %s", providertypes.ModuleName, err)) } + if err := cfg.RegisterMigration(providertypes.ModuleName, 5, m.Migrate5to6); err != nil { + panic(fmt.Sprintf("failed to register migrator for %s: %s", providertypes.ModuleName, err)) + } + } // InitGenesis performs genesis initialization for the provider module. It returns no validator updates. diff --git a/x/ccv/provider/types/genesis_test.go b/x/ccv/provider/types/genesis_test.go index 41a716757f..323aaa49be 100644 --- a/x/ccv/provider/types/genesis_test.go +++ b/x/ccv/provider/types/genesis_test.go @@ -81,7 +81,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -102,7 +102,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -123,7 +123,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -144,7 +144,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -171,7 +171,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -198,7 +198,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -225,7 +225,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(1000000)}, 600), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(1000000)}, 600, 24), nil, nil, nil, @@ -252,7 +252,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -279,7 +279,7 @@ func TestValidateGenesisState(t *testing.T) { 0, // 0 vsc timeout here types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -306,7 +306,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, 0, // 0 slash meter replenish period here types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -333,7 +333,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, "1.15", - sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600), + sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -685,7 +685,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}, 600, 24), nil, nil, nil, @@ -706,7 +706,7 @@ func TestValidateGenesisState(t *testing.T) { nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(-1000000)}, 600), + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(-1000000)}, 600, 24), nil, nil, nil, diff --git a/x/ccv/provider/types/params.go b/x/ccv/provider/types/params.go index a0a7a5ed7a..2c04dd511c 100644 --- a/x/ccv/provider/types/params.go +++ b/x/ccv/provider/types/params.go @@ -41,18 +41,30 @@ const ( // an epoch corresponds to 1 hour (6 * 600 = 3600 seconds). // forcing int64 as the Params KeyTable expects an int64 and not int. DefaultBlocksPerEpoch = int64(600) + + // DefaultNumberOfEpochsToStartReceivingRewards defines the default minimum number of epochs required by a validator to validate + // during so that the validator can start receiving rewards. This would mean that a validator has to be a consumer validator for at least + // `DefaultNumberOfEpochsToStartReceivingRewards * DefaultBlocksPerEpoch` on a consumer chain to start receiving rewards from the chain. + // Note that after a validator starts receiving rewards, the validator would keep receiving rewards every time the + // consumer chain sends an IBC transfer over to the provider. This value only sets a constraint on when a validator + // can first start receiving rewards to avoid cases where a validator just opts in to receive rewards and then opts out + // immediately afterward. + // Current default values for blocks per epoch corresponds to about 1 hour, so with 24 being the + // minimum amount of epochs, this would imply that a validator has to validate at least for 1 day to receive rewards. + DefaultNumberOfEpochsToStartReceivingRewards = int64(24) ) // Reflection based keys for params subspace var ( - KeyTemplateClient = []byte("TemplateClient") - KeyTrustingPeriodFraction = []byte("TrustingPeriodFraction") - KeyInitTimeoutPeriod = []byte("InitTimeoutPeriod") - KeyVscTimeoutPeriod = []byte("VscTimeoutPeriod") - KeySlashMeterReplenishPeriod = []byte("SlashMeterReplenishPeriod") - KeySlashMeterReplenishFraction = []byte("SlashMeterReplenishFraction") - KeyConsumerRewardDenomRegistrationFee = []byte("ConsumerRewardDenomRegistrationFee") - KeyBlocksPerEpoch = []byte("BlocksPerEpoch") + KeyTemplateClient = []byte("TemplateClient") + KeyTrustingPeriodFraction = []byte("TrustingPeriodFraction") + KeyInitTimeoutPeriod = []byte("InitTimeoutPeriod") + KeyVscTimeoutPeriod = []byte("VscTimeoutPeriod") + KeySlashMeterReplenishPeriod = []byte("SlashMeterReplenishPeriod") + KeySlashMeterReplenishFraction = []byte("SlashMeterReplenishFraction") + KeyConsumerRewardDenomRegistrationFee = []byte("ConsumerRewardDenomRegistrationFee") + KeyBlocksPerEpoch = []byte("BlocksPerEpoch") + KeyNumberOfEpochsToStartReceivingRewards = []byte("NumberOfEpochsToStartReceivingRewards") ) // ParamKeyTable returns a key table with the necessary registered provider params @@ -71,17 +83,19 @@ func NewParams( slashMeterReplenishFraction string, consumerRewardDenomRegistrationFee sdk.Coin, blocksPerEpoch int64, + numberOfEpochsToStartReceivingRewards int64, ) Params { return Params{ - TemplateClient: cs, - TrustingPeriodFraction: trustingPeriodFraction, - CcvTimeoutPeriod: ccvTimeoutPeriod, - InitTimeoutPeriod: initTimeoutPeriod, - VscTimeoutPeriod: vscTimeoutPeriod, - SlashMeterReplenishPeriod: slashMeterReplenishPeriod, - SlashMeterReplenishFraction: slashMeterReplenishFraction, - ConsumerRewardDenomRegistrationFee: consumerRewardDenomRegistrationFee, - BlocksPerEpoch: blocksPerEpoch, + TemplateClient: cs, + TrustingPeriodFraction: trustingPeriodFraction, + CcvTimeoutPeriod: ccvTimeoutPeriod, + InitTimeoutPeriod: initTimeoutPeriod, + VscTimeoutPeriod: vscTimeoutPeriod, + SlashMeterReplenishPeriod: slashMeterReplenishPeriod, + SlashMeterReplenishFraction: slashMeterReplenishFraction, + ConsumerRewardDenomRegistrationFee: consumerRewardDenomRegistrationFee, + BlocksPerEpoch: blocksPerEpoch, + NumberOfEpochsToStartReceivingRewards: numberOfEpochsToStartReceivingRewards, } } @@ -113,6 +127,7 @@ func DefaultParams() Params { Amount: sdk.NewInt(10000000), }, DefaultBlocksPerEpoch, + DefaultNumberOfEpochsToStartReceivingRewards, ) } @@ -145,9 +160,12 @@ func (p Params) Validate() error { if err := ValidateCoin(p.ConsumerRewardDenomRegistrationFee); err != nil { return fmt.Errorf("consumer reward denom registration fee is invalid: %s", err) } - if err := ccvtypes.ValidateInt64(p.BlocksPerEpoch); err != nil { + if err := ccvtypes.ValidatePositiveInt64(p.BlocksPerEpoch); err != nil { return fmt.Errorf("blocks per epoch is invalid: %s", err) } + if err := ccvtypes.ValidatePositiveInt64(p.NumberOfEpochsToStartReceivingRewards); err != nil { + return fmt.Errorf("number of epochs to start receiving rewards is invalid: %s", err) + } return nil } @@ -163,6 +181,7 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { paramtypes.NewParamSetPair(KeySlashMeterReplenishFraction, p.SlashMeterReplenishFraction, ccvtypes.ValidateStringFraction), paramtypes.NewParamSetPair(KeyConsumerRewardDenomRegistrationFee, p.ConsumerRewardDenomRegistrationFee, ValidateCoin), paramtypes.NewParamSetPair(KeyBlocksPerEpoch, p.BlocksPerEpoch, ccvtypes.ValidatePositiveInt64), + paramtypes.NewParamSetPair(KeyNumberOfEpochsToStartReceivingRewards, p.NumberOfEpochsToStartReceivingRewards, ccvtypes.ValidatePositiveInt64), } } diff --git a/x/ccv/provider/types/params_test.go b/x/ccv/provider/types/params_test.go index 4e72c233af..bcec7c7c77 100644 --- a/x/ccv/provider/types/params_test.go +++ b/x/ccv/provider/types/params_test.go @@ -24,39 +24,43 @@ func TestValidateParams(t *testing.T) { {"custom valid params", types.NewParams( ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), true}, + "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000, 24), true}, {"custom invalid params", types.NewParams( ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, 0, clienttypes.Height{}, nil, []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000, 24), false}, {"blank client", types.NewParams(&ibctmtypes.ClientState{}, - "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), false}, - {"nil client", types.NewParams(nil, "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000, 24), false}, + {"nil client", types.NewParams(nil, "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000, 24), false}, // Check if "0.00" is valid or if a zero dec TrustFraction needs to return an error {"0 trusting period fraction", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.00", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), true}, + "0.00", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000, 24), true}, {"0 ccv timeout period", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", 0, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), false}, + "0.33", 0, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000, 24), false}, {"0 init timeout period", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, 0, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, 0, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000, 24), false}, {"0 vsc timeout period", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, 0, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, 0, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000, 24), false}, {"0 slash meter replenish period", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, 24*time.Hour, 0, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, 24*time.Hour, 0, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000, 24), false}, {"slash meter replenish fraction over 1", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "1.5", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "1.5", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000, 24), false}, {"invalid consumer reward denom registration fee denom", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}, 1000, 24), false}, {"invalid consumer reward denom registration fee amount", types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(-10000000)}, 1000), false}, + "0.33", time.Hour, time.Hour, 24*time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(-10000000)}, 1000, 24), false}, + {"invalid number of epochs to start receiving rewards", types.NewParams( + ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, + time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), + "0.33", time.Hour, time.Hour, time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 1000, 0), false}, } for _, tc := range testCases { diff --git a/x/ccv/provider/types/provider.pb.go b/x/ccv/provider/types/provider.pb.go index 0540dfb0a6..8649882e18 100644 --- a/x/ccv/provider/types/provider.pb.go +++ b/x/ccv/provider/types/provider.pb.go @@ -593,6 +593,8 @@ type Params struct { ConsumerRewardDenomRegistrationFee types2.Coin `protobuf:"bytes,9,opt,name=consumer_reward_denom_registration_fee,json=consumerRewardDenomRegistrationFee,proto3" json:"consumer_reward_denom_registration_fee"` // The number of blocks that comprise an epoch. BlocksPerEpoch int64 `protobuf:"varint,10,opt,name=blocks_per_epoch,json=blocksPerEpoch,proto3" json:"blocks_per_epoch,omitempty"` + // The number of epochs a validator has to validate a consumer chain in order to start receiving rewards from that chain. + NumberOfEpochsToStartReceivingRewards int64 `protobuf:"varint,11,opt,name=number_of_epochs_to_start_receiving_rewards,json=numberOfEpochsToStartReceivingRewards,proto3" json:"number_of_epochs_to_start_receiving_rewards,omitempty"` } func (m *Params) Reset() { *m = Params{} } @@ -691,6 +693,13 @@ func (m *Params) GetBlocksPerEpoch() int64 { return 0 } +func (m *Params) GetNumberOfEpochsToStartReceivingRewards() int64 { + if m != nil { + return m.NumberOfEpochsToStartReceivingRewards + } + return 0 +} + // SlashAcks contains cons addresses of consumer chain validators // successfully slashed on the provider chain. type SlashAcks struct { @@ -1543,6 +1552,11 @@ type ConsumerValidator struct { Power int64 `protobuf:"varint,2,opt,name=power,proto3" json:"power,omitempty"` // public key the validator uses on the consumer chain during this epoch ConsumerPublicKey *crypto.PublicKey `protobuf:"bytes,3,opt,name=consumer_public_key,json=consumerPublicKey,proto3" json:"consumer_public_key,omitempty"` + // height the validator had when it FIRST became a consumer validator + // If a validator becomes a consumer validator at height `H` and is continuously a consumer validator for all the upcoming + // epochs, then the height of the validator SHOULD remain `H`. This height only resets to a different height if a validator + // stops being a consumer validator during an epoch and later becomes again a consumer validator. + JoinHeight int64 `protobuf:"varint,4,opt,name=join_height,json=joinHeight,proto3" json:"join_height,omitempty"` } func (m *ConsumerValidator) Reset() { *m = ConsumerValidator{} } @@ -1599,6 +1613,13 @@ func (m *ConsumerValidator) GetConsumerPublicKey() *crypto.PublicKey { return nil } +func (m *ConsumerValidator) GetJoinHeight() int64 { + if m != nil { + return m.JoinHeight + } + return 0 +} + // ConsumerRewardsAllocation stores the rewards allocated by a consumer chain // to the consumer rewards pool. It is used to allocate the tokens to the consumer // opted-in validators and the community pool during BeginBlock. @@ -1679,130 +1700,134 @@ func init() { } var fileDescriptor_f22ec409a72b7b72 = []byte{ - // 1954 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcb, 0x6f, 0x1b, 0xc7, - 0x19, 0xd7, 0x92, 0x94, 0x44, 0x0e, 0xf5, 0x1c, 0x29, 0xf1, 0x4a, 0x55, 0x29, 0x7a, 0xd3, 0xa4, - 0x6a, 0x5c, 0x2f, 0x23, 0xa5, 0x05, 0x0c, 0xa3, 0x41, 0x20, 0x51, 0x4e, 0x2c, 0x2b, 0xb6, 0x99, - 0x95, 0x2a, 0xa3, 0xed, 0x61, 0x31, 0x9c, 0x1d, 0x93, 0x03, 0x2d, 0x77, 0xd6, 0x33, 0xc3, 0x55, - 0x78, 0xe9, 0xb9, 0x87, 0x16, 0x48, 0x6f, 0x41, 0x0f, 0x6d, 0x5a, 0xa0, 0x40, 0xd1, 0x4b, 0xfb, - 0x67, 0xe4, 0x98, 0x63, 0x4f, 0x49, 0x61, 0x1f, 0x7a, 0xe8, 0x3f, 0x51, 0xcc, 0xec, 0x93, 0x94, - 0xe4, 0xd2, 0x48, 0x72, 0x91, 0x76, 0xbf, 0xc7, 0xef, 0xfb, 0x66, 0xbe, 0x27, 0x17, 0xec, 0xd1, - 0x40, 0x12, 0x8e, 0xfb, 0x88, 0x06, 0xae, 0x20, 0x78, 0xc8, 0xa9, 0x1c, 0xb5, 0x30, 0x8e, 0x5a, - 0x21, 0x67, 0x11, 0xf5, 0x08, 0x6f, 0x45, 0xbb, 0xd9, 0xb3, 0x1d, 0x72, 0x26, 0x19, 0x7c, 0xe3, - 0x0a, 0x1d, 0x1b, 0xe3, 0xc8, 0xce, 0xe4, 0xa2, 0xdd, 0xcd, 0x37, 0xaf, 0x03, 0x8e, 0x76, 0x5b, - 0x17, 0x94, 0x93, 0x18, 0x6b, 0x73, 0xbd, 0xc7, 0x7a, 0x4c, 0x3f, 0xb6, 0xd4, 0x53, 0x42, 0xdd, - 0xee, 0x31, 0xd6, 0xf3, 0x49, 0x4b, 0xbf, 0x75, 0x87, 0x4f, 0x5b, 0x92, 0x0e, 0x88, 0x90, 0x68, - 0x10, 0x26, 0x02, 0x8d, 0x49, 0x01, 0x6f, 0xc8, 0x91, 0xa4, 0x2c, 0x48, 0x01, 0x68, 0x17, 0xb7, - 0x30, 0xe3, 0xa4, 0x85, 0x7d, 0x4a, 0x02, 0xa9, 0xac, 0xc6, 0x4f, 0x89, 0x40, 0x4b, 0x09, 0xf8, - 0xb4, 0xd7, 0x97, 0x31, 0x59, 0xb4, 0x24, 0x09, 0x3c, 0xc2, 0x07, 0x34, 0x16, 0xce, 0xdf, 0x12, - 0x85, 0xad, 0x02, 0x1f, 0xf3, 0x51, 0x28, 0x59, 0xeb, 0x9c, 0x8c, 0x44, 0xc2, 0x7d, 0x0b, 0x33, - 0x31, 0x60, 0xa2, 0x45, 0xd4, 0xf9, 0x03, 0x4c, 0x5a, 0xd1, 0x6e, 0x97, 0x48, 0xb4, 0x9b, 0x11, - 0x52, 0xbf, 0x13, 0xb9, 0x2e, 0x12, 0xb9, 0x0c, 0x66, 0x34, 0xf5, 0x7b, 0x15, 0x0d, 0x68, 0xc0, - 0x5a, 0xfa, 0x6f, 0x4c, 0xb2, 0x7e, 0x5b, 0x05, 0x66, 0x9b, 0x05, 0x62, 0x38, 0x20, 0x7c, 0xdf, - 0xf3, 0xa8, 0x3a, 0x65, 0x87, 0xb3, 0x90, 0x09, 0xe4, 0xc3, 0x75, 0x30, 0x2b, 0xa9, 0xf4, 0x89, - 0x69, 0x34, 0x8d, 0x9d, 0x9a, 0x13, 0xbf, 0xc0, 0x26, 0xa8, 0x7b, 0x44, 0x60, 0x4e, 0x43, 0x25, - 0x6c, 0x96, 0x34, 0xaf, 0x48, 0x82, 0x1b, 0xa0, 0x1a, 0x87, 0x86, 0x7a, 0x66, 0x59, 0xb3, 0xe7, - 0xf5, 0xfb, 0x91, 0x07, 0x3f, 0x04, 0x4b, 0x34, 0xa0, 0x92, 0x22, 0xdf, 0xed, 0x13, 0x75, 0x41, - 0x66, 0xa5, 0x69, 0xec, 0xd4, 0xf7, 0x36, 0x6d, 0xda, 0xc5, 0xb6, 0xba, 0x53, 0x3b, 0xb9, 0xc9, - 0x68, 0xd7, 0xbe, 0xaf, 0x25, 0x0e, 0x2a, 0x5f, 0x7c, 0xb5, 0x3d, 0xe3, 0x2c, 0x26, 0x7a, 0x31, - 0x11, 0xde, 0x04, 0x0b, 0x3d, 0x12, 0x10, 0x41, 0x85, 0xdb, 0x47, 0xa2, 0x6f, 0xce, 0x36, 0x8d, - 0x9d, 0x05, 0xa7, 0x9e, 0xd0, 0xee, 0x23, 0xd1, 0x87, 0xdb, 0xa0, 0xde, 0xa5, 0x01, 0xe2, 0xa3, - 0x58, 0x62, 0x4e, 0x4b, 0x80, 0x98, 0xa4, 0x05, 0xda, 0x00, 0x88, 0x10, 0x5d, 0x04, 0xae, 0x4a, - 0x00, 0x73, 0x3e, 0x71, 0x24, 0x0e, 0xbe, 0x9d, 0x06, 0xdf, 0x3e, 0x4d, 0xb3, 0xe3, 0xa0, 0xaa, - 0x1c, 0xf9, 0xf4, 0xeb, 0x6d, 0xc3, 0xa9, 0x69, 0x3d, 0xc5, 0x81, 0x8f, 0xc0, 0xca, 0x30, 0xe8, - 0xb2, 0xc0, 0xa3, 0x41, 0xcf, 0x0d, 0x09, 0xa7, 0xcc, 0x33, 0xab, 0x1a, 0x6a, 0xe3, 0x12, 0xd4, - 0x61, 0x92, 0x47, 0x31, 0xd2, 0x67, 0x0a, 0x69, 0x39, 0x53, 0xee, 0x68, 0x5d, 0xf8, 0x31, 0x80, - 0x18, 0x47, 0xda, 0x25, 0x36, 0x94, 0x29, 0x62, 0x6d, 0x7a, 0xc4, 0x15, 0x8c, 0xa3, 0xd3, 0x58, - 0x3b, 0x81, 0xfc, 0x15, 0xb8, 0x21, 0x39, 0x0a, 0xc4, 0x53, 0xc2, 0x27, 0x71, 0xc1, 0xf4, 0xb8, - 0xaf, 0xa5, 0x18, 0xe3, 0xe0, 0xf7, 0x41, 0x13, 0x27, 0x09, 0xe4, 0x72, 0xe2, 0x51, 0x21, 0x39, - 0xed, 0x0e, 0x95, 0xae, 0xfb, 0x94, 0x23, 0xac, 0x73, 0xa4, 0xae, 0x93, 0xa0, 0x91, 0xca, 0x39, - 0x63, 0x62, 0x1f, 0x24, 0x52, 0xf0, 0x31, 0xf8, 0x41, 0xd7, 0x67, 0xf8, 0x5c, 0x28, 0xe7, 0xdc, - 0x31, 0x24, 0x6d, 0x7a, 0x40, 0x85, 0x50, 0x68, 0x0b, 0x4d, 0x63, 0xa7, 0xec, 0xdc, 0x8c, 0x65, - 0x3b, 0x84, 0x1f, 0x16, 0x24, 0x4f, 0x0b, 0x82, 0xf0, 0x36, 0x80, 0x7d, 0x2a, 0x24, 0xe3, 0x14, - 0x23, 0xdf, 0x25, 0x81, 0xe4, 0x94, 0x08, 0x73, 0x51, 0xab, 0xaf, 0xe6, 0x9c, 0x7b, 0x31, 0x03, - 0x3e, 0x00, 0x37, 0xaf, 0x35, 0xea, 0xe2, 0x3e, 0x0a, 0x02, 0xe2, 0x9b, 0x4b, 0xfa, 0x28, 0xdb, - 0xde, 0x35, 0x36, 0xdb, 0xb1, 0x18, 0x5c, 0x03, 0xb3, 0x92, 0x85, 0xee, 0x23, 0x73, 0xb9, 0x69, - 0xec, 0x2c, 0x3a, 0x15, 0xc9, 0xc2, 0x47, 0xf0, 0x1d, 0xb0, 0x1e, 0x21, 0x9f, 0x7a, 0x48, 0x32, - 0x2e, 0xdc, 0x90, 0x5d, 0x10, 0xee, 0x62, 0x14, 0x9a, 0x2b, 0x5a, 0x06, 0xe6, 0xbc, 0x8e, 0x62, - 0xb5, 0x51, 0x08, 0xdf, 0x06, 0xab, 0x19, 0xd5, 0x15, 0x44, 0x6a, 0xf1, 0x55, 0x2d, 0xbe, 0x9c, - 0x31, 0x4e, 0x88, 0x54, 0xb2, 0x5b, 0xa0, 0x86, 0x7c, 0x9f, 0x5d, 0xf8, 0x54, 0x48, 0x13, 0x36, - 0xcb, 0x3b, 0x35, 0x27, 0x27, 0xc0, 0x4d, 0x50, 0xf5, 0x48, 0x30, 0xd2, 0xcc, 0x35, 0xcd, 0xcc, - 0xde, 0xef, 0x56, 0x7f, 0xf3, 0xf9, 0xf6, 0xcc, 0x67, 0x9f, 0x6f, 0xcf, 0x58, 0xff, 0x30, 0xc0, - 0x8d, 0x76, 0x16, 0xa5, 0x01, 0x8b, 0x90, 0xff, 0x5d, 0x76, 0x83, 0x7d, 0x50, 0x13, 0xea, 0x9a, - 0x74, 0xfd, 0x55, 0x5e, 0xa1, 0xfe, 0xaa, 0x4a, 0x4d, 0x31, 0xac, 0x3f, 0x96, 0xc0, 0x56, 0xea, - 0xf1, 0x43, 0xe6, 0xd1, 0xa7, 0x14, 0xa3, 0xef, 0xba, 0x89, 0x65, 0xc1, 0xad, 0x4c, 0x11, 0xdc, - 0xd9, 0x57, 0x0b, 0xee, 0xdc, 0x14, 0xc1, 0x9d, 0x7f, 0x59, 0x70, 0xab, 0xe3, 0xc1, 0xb5, 0xfe, - 0x64, 0x80, 0xf5, 0x7b, 0xcf, 0x86, 0x34, 0x62, 0xdf, 0xd2, 0xc5, 0x1c, 0x83, 0x45, 0x52, 0xc0, - 0x13, 0x66, 0xb9, 0x59, 0xde, 0xa9, 0xef, 0xbd, 0x69, 0xc7, 0xd3, 0xc7, 0xce, 0x86, 0x52, 0x32, - 0x81, 0xec, 0xa2, 0x75, 0x67, 0x5c, 0xf7, 0x6e, 0xc9, 0x34, 0xac, 0xbf, 0x18, 0x60, 0x53, 0xd5, - 0x4d, 0x8f, 0x38, 0xe4, 0x02, 0x71, 0xef, 0x90, 0x04, 0x6c, 0x20, 0xbe, 0xb1, 0x9f, 0x16, 0x58, - 0xf4, 0x34, 0x92, 0x2b, 0x99, 0x8b, 0x3c, 0x4f, 0xfb, 0xa9, 0x65, 0x14, 0xf1, 0x94, 0xed, 0x7b, - 0x1e, 0xdc, 0x01, 0x2b, 0xb9, 0x0c, 0x57, 0x09, 0xaf, 0xf2, 0x50, 0x89, 0x2d, 0xa5, 0x62, 0xba, - 0x0c, 0x88, 0xf5, 0x5f, 0x03, 0xac, 0x7c, 0xe8, 0xb3, 0x2e, 0xf2, 0x4f, 0x7c, 0x24, 0xfa, 0xaa, - 0x67, 0x8c, 0x54, 0xfe, 0x72, 0x92, 0x34, 0x6b, 0xed, 0xde, 0xd4, 0xf9, 0xab, 0xd4, 0xf4, 0xf8, - 0x78, 0x1f, 0xac, 0x66, 0xed, 0x33, 0xcb, 0x37, 0x7d, 0x9a, 0x83, 0xb5, 0xe7, 0x5f, 0x6d, 0x2f, - 0xa7, 0xb9, 0xdd, 0xd6, 0xb9, 0x77, 0xe8, 0x2c, 0xe3, 0x31, 0x82, 0x07, 0x1b, 0xa0, 0x4e, 0xbb, - 0xd8, 0x15, 0xe4, 0x99, 0x1b, 0x0c, 0x07, 0x3a, 0x55, 0x2b, 0x4e, 0x8d, 0x76, 0xf1, 0x09, 0x79, - 0xf6, 0x68, 0x38, 0x80, 0xef, 0x82, 0xd7, 0xd3, 0xcd, 0xc9, 0x8d, 0x90, 0xef, 0x2a, 0x7d, 0x75, - 0x1d, 0x5c, 0x67, 0xef, 0x82, 0xb3, 0x96, 0x72, 0xcf, 0x90, 0xaf, 0x8c, 0xed, 0x7b, 0x1e, 0xb7, - 0x5e, 0xcc, 0x82, 0xb9, 0x0e, 0xe2, 0x68, 0x20, 0xe0, 0x29, 0x58, 0x96, 0x64, 0x10, 0xfa, 0x48, - 0x12, 0x37, 0x1e, 0xcd, 0xc9, 0x49, 0x6f, 0xe9, 0x91, 0x5d, 0xdc, 0x72, 0xec, 0xc2, 0x5e, 0x13, - 0xed, 0xda, 0x6d, 0x4d, 0x3d, 0x91, 0x48, 0x12, 0x67, 0x29, 0xc5, 0x88, 0x89, 0xf0, 0x0e, 0x30, - 0x25, 0x1f, 0x0a, 0x99, 0x0f, 0xcd, 0x7c, 0x5a, 0xc4, 0xb1, 0x7c, 0x3d, 0xe5, 0xc7, 0x73, 0x26, - 0x9b, 0x12, 0x57, 0xcf, 0xc7, 0xf2, 0x37, 0x99, 0x8f, 0x27, 0x60, 0x4d, 0x2d, 0x17, 0x93, 0x98, - 0x95, 0xe9, 0x31, 0x57, 0x95, 0xfe, 0x38, 0xe8, 0xc7, 0x00, 0x46, 0x02, 0x4f, 0x62, 0xce, 0xbe, - 0x82, 0x9f, 0x91, 0xc0, 0xe3, 0x90, 0x1e, 0xd8, 0x12, 0x2a, 0xf9, 0xdc, 0x01, 0x91, 0x7a, 0xda, - 0x86, 0x3e, 0x09, 0xa8, 0xe8, 0xa7, 0xe0, 0x73, 0xd3, 0x83, 0x6f, 0x68, 0xa0, 0x87, 0x0a, 0xc7, - 0x49, 0x61, 0x12, 0x2b, 0x6d, 0xd0, 0xb8, 0xda, 0x4a, 0x16, 0xa0, 0x79, 0x1d, 0xa0, 0xef, 0x5d, - 0x01, 0x91, 0x45, 0x49, 0x80, 0xb7, 0x0a, 0x5b, 0x81, 0xaa, 0x6a, 0x57, 0x17, 0x94, 0xcb, 0x49, - 0x4f, 0x8d, 0x4e, 0x14, 0x2f, 0x08, 0x84, 0x64, 0x9b, 0x4d, 0xd2, 0x3d, 0xd4, 0xee, 0x9a, 0x75, - 0x8e, 0x36, 0xa3, 0x41, 0xb2, 0xfe, 0x59, 0xf9, 0xf2, 0x90, 0xf5, 0x08, 0xa7, 0x80, 0xf5, 0x01, - 0x21, 0xaa, 0x9a, 0x0b, 0x0b, 0x04, 0x09, 0x19, 0xee, 0xeb, 0x05, 0xa7, 0xec, 0x2c, 0x65, 0xcb, - 0xc2, 0x3d, 0x45, 0x7d, 0x50, 0xa9, 0x56, 0x57, 0x6a, 0xd6, 0x8f, 0x40, 0x4d, 0x17, 0xf3, 0x3e, - 0x3e, 0x17, 0xba, 0xc3, 0x7a, 0x1e, 0x27, 0x42, 0x10, 0x61, 0x1a, 0x49, 0x87, 0x4d, 0x09, 0x96, - 0x04, 0x1b, 0xd7, 0xad, 0xc9, 0x02, 0x3e, 0x01, 0xf3, 0x21, 0xd1, 0x3b, 0x9c, 0x56, 0xac, 0xef, - 0xbd, 0x67, 0x4f, 0xf1, 0x23, 0xc6, 0xbe, 0x0e, 0xd0, 0x49, 0xd1, 0x2c, 0x9e, 0x2f, 0xe7, 0x13, - 0xd3, 0x58, 0xc0, 0xb3, 0x49, 0xa3, 0x3f, 0x7b, 0x25, 0xa3, 0x13, 0x78, 0xb9, 0xcd, 0x5b, 0xa0, - 0xbe, 0x1f, 0x1f, 0xfb, 0x23, 0x35, 0x5a, 0x2e, 0x5d, 0xcb, 0x42, 0xf1, 0x5a, 0x1e, 0x80, 0xa5, - 0x64, 0xe3, 0x39, 0x65, 0xba, 0x21, 0xc1, 0xef, 0x03, 0x90, 0xac, 0x4a, 0xaa, 0x91, 0xc5, 0x2d, - 0xbb, 0x96, 0x50, 0x8e, 0xbc, 0xb1, 0xa9, 0x5a, 0x1a, 0x9b, 0xaa, 0x96, 0x03, 0x96, 0xcf, 0x04, - 0xfe, 0x79, 0xba, 0x0e, 0x3f, 0x0e, 0x05, 0x7c, 0x0d, 0xcc, 0xa9, 0x1a, 0x4a, 0x80, 0x2a, 0xce, - 0x6c, 0x24, 0xf0, 0x91, 0xee, 0xda, 0xf9, 0xca, 0xcd, 0x42, 0x97, 0x7a, 0xc2, 0x2c, 0x35, 0xcb, - 0x3b, 0x15, 0x67, 0x69, 0x98, 0xab, 0x1f, 0x79, 0xc2, 0xfa, 0x05, 0xa8, 0x17, 0x00, 0xe1, 0x12, - 0x28, 0x65, 0x58, 0x25, 0xea, 0xc1, 0xbb, 0x60, 0x23, 0x07, 0x1a, 0x6f, 0xc3, 0x31, 0x62, 0xcd, - 0xb9, 0x91, 0x09, 0x8c, 0x75, 0x62, 0x61, 0x3d, 0x06, 0xeb, 0x47, 0x79, 0xd1, 0x67, 0x4d, 0x7e, - 0xec, 0x84, 0xc6, 0xf8, 0xde, 0xb0, 0x05, 0x6a, 0xd9, 0x4f, 0x4d, 0x7d, 0xfa, 0x8a, 0x93, 0x13, - 0xac, 0x01, 0x58, 0x39, 0x13, 0xf8, 0x84, 0x04, 0x5e, 0x0e, 0x76, 0xcd, 0x05, 0x1c, 0x4c, 0x02, - 0x4d, 0xfd, 0xbb, 0x25, 0x37, 0xc7, 0xc0, 0xc6, 0x59, 0x71, 0xc9, 0xd0, 0x03, 0xb8, 0x83, 0xf0, - 0x39, 0x91, 0x02, 0x3a, 0xa0, 0xa2, 0x97, 0x89, 0x38, 0xb3, 0xee, 0x5c, 0x9b, 0x59, 0xd1, 0xae, - 0x7d, 0x1d, 0xc8, 0x21, 0x92, 0x28, 0xa9, 0x5d, 0x8d, 0x65, 0xfd, 0x10, 0xac, 0x3d, 0x44, 0x72, - 0xc8, 0x89, 0x37, 0x16, 0xe3, 0x15, 0x50, 0x56, 0xf1, 0x33, 0x74, 0xfc, 0xd4, 0xa3, 0xda, 0x07, - 0xcc, 0x7b, 0x9f, 0x84, 0x8c, 0x4b, 0xe2, 0x5d, 0xba, 0x91, 0x97, 0x5c, 0xef, 0x39, 0x58, 0x53, - 0x97, 0x25, 0x48, 0xe0, 0xb9, 0xd9, 0x39, 0xe3, 0x38, 0xd6, 0xf7, 0x7e, 0x3a, 0x55, 0x75, 0x4c, - 0x9a, 0x4b, 0x0e, 0xb0, 0x1a, 0x4d, 0xd0, 0x85, 0xf5, 0x7b, 0x03, 0x98, 0xc7, 0x64, 0xb4, 0x2f, - 0x04, 0xed, 0x05, 0x03, 0x12, 0x48, 0xd5, 0x03, 0x11, 0x26, 0xea, 0x11, 0xbe, 0x01, 0x16, 0xb3, - 0x99, 0xab, 0x47, 0xad, 0xa1, 0x47, 0xed, 0x42, 0x4a, 0x54, 0x05, 0x06, 0xef, 0x02, 0x10, 0x72, - 0x12, 0xb9, 0xd8, 0x3d, 0x27, 0xa3, 0x24, 0x8a, 0x5b, 0xc5, 0x11, 0x1a, 0x7f, 0x08, 0xb0, 0x3b, - 0xc3, 0xae, 0x4f, 0xf1, 0x31, 0x19, 0x39, 0x55, 0x25, 0xdf, 0x3e, 0x26, 0x23, 0xb5, 0x13, 0xe9, - 0x0d, 0x53, 0xcf, 0xbd, 0xb2, 0x13, 0xbf, 0x58, 0x7f, 0x30, 0xc0, 0x8d, 0x2c, 0x1c, 0x69, 0xba, - 0x76, 0x86, 0x5d, 0xa5, 0xf1, 0x92, 0x7b, 0xbb, 0xe4, 0x6d, 0xe9, 0x0a, 0x6f, 0xdf, 0x07, 0x0b, - 0x59, 0x81, 0x28, 0x7f, 0xcb, 0x53, 0xf8, 0x5b, 0x4f, 0x35, 0x8e, 0xc9, 0xc8, 0xfa, 0x75, 0xc1, - 0xb7, 0x83, 0x51, 0xa1, 0xf7, 0xf1, 0xff, 0xe3, 0x5b, 0x66, 0xb6, 0xe8, 0x1b, 0x2e, 0xea, 0x5f, - 0x3a, 0x40, 0xf9, 0xf2, 0x01, 0xac, 0x3f, 0x1b, 0x60, 0xbd, 0x68, 0x55, 0x9c, 0xb2, 0x0e, 0x1f, - 0x06, 0xe4, 0x65, 0xd6, 0xf3, 0xf2, 0x2b, 0x15, 0xcb, 0xef, 0x09, 0x58, 0x1a, 0x73, 0x4a, 0x24, - 0xb7, 0xf1, 0xce, 0x54, 0x39, 0x56, 0xe8, 0xae, 0xce, 0x62, 0xf1, 0x1c, 0xc2, 0xfa, 0xab, 0x01, - 0x56, 0x53, 0x1f, 0xb3, 0xcb, 0x82, 0x3f, 0x06, 0x30, 0x3b, 0x5e, 0xbe, 0xbd, 0xc5, 0x29, 0xb5, - 0x92, 0x72, 0xd2, 0xd5, 0x2d, 0x4f, 0x8d, 0x52, 0x21, 0x35, 0xe0, 0x47, 0x60, 0x2d, 0x73, 0x39, - 0xd4, 0x01, 0x9a, 0x3a, 0x8a, 0xd9, 0x7e, 0x9a, 0x91, 0xac, 0xdf, 0x19, 0xf9, 0x38, 0x8c, 0xe7, - 0xb1, 0xd8, 0xf7, 0xfd, 0x64, 0xa9, 0x87, 0x21, 0x98, 0x8f, 0x47, 0xbe, 0x48, 0xfa, 0xc7, 0xd6, - 0x95, 0xc3, 0xfd, 0x90, 0x60, 0x3d, 0xdf, 0xef, 0xa8, 0x12, 0xfb, 0xfb, 0xd7, 0xdb, 0xb7, 0x7a, - 0x54, 0xf6, 0x87, 0x5d, 0x1b, 0xb3, 0x41, 0x2b, 0xf9, 0x90, 0x15, 0xff, 0xbb, 0x2d, 0xbc, 0xf3, - 0x96, 0x1c, 0x85, 0x44, 0xa4, 0x3a, 0xe2, 0x6f, 0xff, 0xf9, 0xe7, 0xdb, 0x86, 0x93, 0x9a, 0x39, - 0x78, 0xf2, 0xc5, 0xf3, 0x86, 0xf1, 0xe5, 0xf3, 0x86, 0xf1, 0xef, 0xe7, 0x0d, 0xe3, 0xd3, 0x17, - 0x8d, 0x99, 0x2f, 0x5f, 0x34, 0x66, 0xfe, 0xf5, 0xa2, 0x31, 0xf3, 0xcb, 0xf7, 0x2e, 0x83, 0xe6, - 0x31, 0xba, 0x9d, 0x7d, 0x3a, 0x8c, 0x7e, 0xd2, 0xfa, 0x64, 0xfc, 0xc3, 0xa4, 0xb6, 0xd7, 0x9d, - 0xd3, 0xdd, 0xf4, 0xdd, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x24, 0x1f, 0x07, 0x8b, 0xc9, 0x14, - 0x00, 0x00, + // 2022 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcd, 0x73, 0x1c, 0x47, + 0x15, 0xd7, 0x68, 0x57, 0xd2, 0xee, 0x5b, 0x7d, 0xb6, 0x94, 0x78, 0x64, 0x84, 0x24, 0x4f, 0x70, + 0x10, 0x31, 0xde, 0x8d, 0x1c, 0xa8, 0x72, 0xb9, 0x48, 0xa5, 0xa4, 0x95, 0x13, 0x7f, 0xc4, 0xb6, + 0x32, 0x12, 0x76, 0x11, 0x0e, 0x53, 0xbd, 0x3d, 0xad, 0xdd, 0x46, 0xb3, 0xd3, 0xe3, 0xee, 0xde, + 0x71, 0xf6, 0xc2, 0x99, 0x03, 0x54, 0x85, 0x5b, 0x8a, 0x03, 0x04, 0x4e, 0x14, 0x17, 0xf8, 0x0f, + 0xb8, 0x51, 0x39, 0xe6, 0xc8, 0x29, 0xa1, 0xec, 0x03, 0x07, 0xfe, 0x09, 0xaa, 0x7b, 0x3e, 0x77, + 0x25, 0x99, 0x75, 0x85, 0x5c, 0xa4, 0x99, 0xd7, 0xbf, 0xf7, 0x7b, 0xaf, 0xfb, 0xbd, 0x7e, 0xef, + 0xed, 0xc0, 0x0d, 0x16, 0x2a, 0x2a, 0x48, 0x0f, 0xb3, 0xd0, 0x93, 0x94, 0x0c, 0x04, 0x53, 0xc3, + 0x16, 0x21, 0x71, 0x2b, 0x12, 0x3c, 0x66, 0x3e, 0x15, 0xad, 0x78, 0x37, 0x7f, 0x6e, 0x46, 0x82, + 0x2b, 0x8e, 0xde, 0x38, 0x47, 0xa7, 0x49, 0x48, 0xdc, 0xcc, 0x71, 0xf1, 0xee, 0xe5, 0xab, 0x17, + 0x11, 0xc7, 0xbb, 0xad, 0x67, 0x4c, 0xd0, 0x84, 0xeb, 0xf2, 0x5a, 0x97, 0x77, 0xb9, 0x79, 0x6c, + 0xe9, 0xa7, 0x54, 0xba, 0xd5, 0xe5, 0xbc, 0x1b, 0xd0, 0x96, 0x79, 0xeb, 0x0c, 0x4e, 0x5a, 0x8a, + 0xf5, 0xa9, 0x54, 0xb8, 0x1f, 0xa5, 0x80, 0xcd, 0x71, 0x80, 0x3f, 0x10, 0x58, 0x31, 0x1e, 0x66, + 0x04, 0xac, 0x43, 0x5a, 0x84, 0x0b, 0xda, 0x22, 0x01, 0xa3, 0xa1, 0xd2, 0x56, 0x93, 0xa7, 0x14, + 0xd0, 0xd2, 0x80, 0x80, 0x75, 0x7b, 0x2a, 0x11, 0xcb, 0x96, 0xa2, 0xa1, 0x4f, 0x45, 0x9f, 0x25, + 0xe0, 0xe2, 0x2d, 0x55, 0xd8, 0x28, 0xad, 0x13, 0x31, 0x8c, 0x14, 0x6f, 0x9d, 0xd2, 0xa1, 0x4c, + 0x57, 0xdf, 0x24, 0x5c, 0xf6, 0xb9, 0x6c, 0x51, 0xbd, 0xff, 0x90, 0xd0, 0x56, 0xbc, 0xdb, 0xa1, + 0x0a, 0xef, 0xe6, 0x82, 0xcc, 0xef, 0x14, 0xd7, 0xc1, 0xb2, 0xc0, 0x10, 0xce, 0x32, 0xbf, 0x57, + 0x70, 0x9f, 0x85, 0xbc, 0x65, 0xfe, 0x26, 0x22, 0xe7, 0xd7, 0x35, 0xb0, 0xdb, 0x3c, 0x94, 0x83, + 0x3e, 0x15, 0x7b, 0xbe, 0xcf, 0xf4, 0x2e, 0x0f, 0x05, 0x8f, 0xb8, 0xc4, 0x01, 0x5a, 0x83, 0x19, + 0xc5, 0x54, 0x40, 0x6d, 0x6b, 0xdb, 0xda, 0xa9, 0xbb, 0xc9, 0x0b, 0xda, 0x86, 0x86, 0x4f, 0x25, + 0x11, 0x2c, 0xd2, 0x60, 0x7b, 0xda, 0xac, 0x95, 0x45, 0x68, 0x1d, 0x6a, 0x49, 0x68, 0x98, 0x6f, + 0x57, 0xcc, 0xf2, 0x9c, 0x79, 0xbf, 0xeb, 0xa3, 0x0f, 0x60, 0x91, 0x85, 0x4c, 0x31, 0x1c, 0x78, + 0x3d, 0xaa, 0x0f, 0xc8, 0xae, 0x6e, 0x5b, 0x3b, 0x8d, 0x1b, 0x97, 0x9b, 0xac, 0x43, 0x9a, 0xfa, + 0x4c, 0x9b, 0xe9, 0x49, 0xc6, 0xbb, 0xcd, 0x3b, 0x06, 0xb1, 0x5f, 0xfd, 0xe2, 0xab, 0xad, 0x29, + 0x77, 0x21, 0xd5, 0x4b, 0x84, 0xe8, 0x0a, 0xcc, 0x77, 0x69, 0x48, 0x25, 0x93, 0x5e, 0x0f, 0xcb, + 0x9e, 0x3d, 0xb3, 0x6d, 0xed, 0xcc, 0xbb, 0x8d, 0x54, 0x76, 0x07, 0xcb, 0x1e, 0xda, 0x82, 0x46, + 0x87, 0x85, 0x58, 0x0c, 0x13, 0xc4, 0xac, 0x41, 0x40, 0x22, 0x32, 0x80, 0x36, 0x80, 0x8c, 0xf0, + 0xb3, 0xd0, 0xd3, 0x09, 0x60, 0xcf, 0xa5, 0x8e, 0x24, 0xc1, 0x6f, 0x66, 0xc1, 0x6f, 0x1e, 0x67, + 0xd9, 0xb1, 0x5f, 0xd3, 0x8e, 0x7c, 0xfa, 0xf5, 0x96, 0xe5, 0xd6, 0x8d, 0x9e, 0x5e, 0x41, 0x0f, + 0x61, 0x79, 0x10, 0x76, 0x78, 0xe8, 0xb3, 0xb0, 0xeb, 0x45, 0x54, 0x30, 0xee, 0xdb, 0x35, 0x43, + 0xb5, 0x7e, 0x86, 0xea, 0x20, 0xcd, 0xa3, 0x84, 0xe9, 0x33, 0xcd, 0xb4, 0x94, 0x2b, 0x1f, 0x1a, + 0x5d, 0xf4, 0x11, 0x20, 0x42, 0x62, 0xe3, 0x12, 0x1f, 0xa8, 0x8c, 0xb1, 0x3e, 0x39, 0xe3, 0x32, + 0x21, 0xf1, 0x71, 0xa2, 0x9d, 0x52, 0xfe, 0x1c, 0x2e, 0x29, 0x81, 0x43, 0x79, 0x42, 0xc5, 0x38, + 0x2f, 0x4c, 0xce, 0xfb, 0x5a, 0xc6, 0x31, 0x4a, 0x7e, 0x07, 0xb6, 0x49, 0x9a, 0x40, 0x9e, 0xa0, + 0x3e, 0x93, 0x4a, 0xb0, 0xce, 0x40, 0xeb, 0x7a, 0x27, 0x02, 0x13, 0x93, 0x23, 0x0d, 0x93, 0x04, + 0x9b, 0x19, 0xce, 0x1d, 0x81, 0xbd, 0x9f, 0xa2, 0xd0, 0x23, 0xf8, 0x5e, 0x27, 0xe0, 0xe4, 0x54, + 0x6a, 0xe7, 0xbc, 0x11, 0x26, 0x63, 0xba, 0xcf, 0xa4, 0xd4, 0x6c, 0xf3, 0xdb, 0xd6, 0x4e, 0xc5, + 0xbd, 0x92, 0x60, 0x0f, 0xa9, 0x38, 0x28, 0x21, 0x8f, 0x4b, 0x40, 0x74, 0x1d, 0x50, 0x8f, 0x49, + 0xc5, 0x05, 0x23, 0x38, 0xf0, 0x68, 0xa8, 0x04, 0xa3, 0xd2, 0x5e, 0x30, 0xea, 0x2b, 0xc5, 0xca, + 0xed, 0x64, 0x01, 0xdd, 0x83, 0x2b, 0x17, 0x1a, 0xf5, 0x48, 0x0f, 0x87, 0x21, 0x0d, 0xec, 0x45, + 0xb3, 0x95, 0x2d, 0xff, 0x02, 0x9b, 0xed, 0x04, 0x86, 0x56, 0x61, 0x46, 0xf1, 0xc8, 0x7b, 0x68, + 0x2f, 0x6d, 0x5b, 0x3b, 0x0b, 0x6e, 0x55, 0xf1, 0xe8, 0x21, 0x7a, 0x1b, 0xd6, 0x62, 0x1c, 0x30, + 0x1f, 0x2b, 0x2e, 0xa4, 0x17, 0xf1, 0x67, 0x54, 0x78, 0x04, 0x47, 0xf6, 0xb2, 0xc1, 0xa0, 0x62, + 0xed, 0x50, 0x2f, 0xb5, 0x71, 0x84, 0xde, 0x82, 0x95, 0x5c, 0xea, 0x49, 0xaa, 0x0c, 0x7c, 0xc5, + 0xc0, 0x97, 0xf2, 0x85, 0x23, 0xaa, 0x34, 0x76, 0x03, 0xea, 0x38, 0x08, 0xf8, 0xb3, 0x80, 0x49, + 0x65, 0xa3, 0xed, 0xca, 0x4e, 0xdd, 0x2d, 0x04, 0xe8, 0x32, 0xd4, 0x7c, 0x1a, 0x0e, 0xcd, 0xe2, + 0xaa, 0x59, 0xcc, 0xdf, 0x6f, 0xd5, 0x7e, 0xf5, 0xf9, 0xd6, 0xd4, 0x67, 0x9f, 0x6f, 0x4d, 0x39, + 0x7f, 0xb5, 0xe0, 0x52, 0x3b, 0x8f, 0x52, 0x9f, 0xc7, 0x38, 0xf8, 0x36, 0xab, 0xc1, 0x1e, 0xd4, + 0xa5, 0x3e, 0x26, 0x73, 0xff, 0xaa, 0xaf, 0x70, 0xff, 0x6a, 0x5a, 0x4d, 0x2f, 0x38, 0xbf, 0x9f, + 0x86, 0x8d, 0xcc, 0xe3, 0x07, 0xdc, 0x67, 0x27, 0x8c, 0xe0, 0x6f, 0xbb, 0x88, 0xe5, 0xc1, 0xad, + 0x4e, 0x10, 0xdc, 0x99, 0x57, 0x0b, 0xee, 0xec, 0x04, 0xc1, 0x9d, 0x7b, 0x59, 0x70, 0x6b, 0xa3, + 0xc1, 0x75, 0xfe, 0x60, 0xc1, 0xda, 0xed, 0xa7, 0x03, 0x16, 0xf3, 0xff, 0xd3, 0xc1, 0xdc, 0x87, + 0x05, 0x5a, 0xe2, 0x93, 0x76, 0x65, 0xbb, 0xb2, 0xd3, 0xb8, 0x71, 0xb5, 0x99, 0x74, 0x9f, 0x66, + 0xde, 0x94, 0xd2, 0x0e, 0xd4, 0x2c, 0x5b, 0x77, 0x47, 0x75, 0x6f, 0x4d, 0xdb, 0x96, 0xf3, 0x27, + 0x0b, 0x2e, 0xeb, 0x7b, 0xd3, 0xa5, 0x2e, 0x7d, 0x86, 0x85, 0x7f, 0x40, 0x43, 0xde, 0x97, 0xdf, + 0xd8, 0x4f, 0x07, 0x16, 0x7c, 0xc3, 0xe4, 0x29, 0xee, 0x61, 0xdf, 0x37, 0x7e, 0x1a, 0x8c, 0x16, + 0x1e, 0xf3, 0x3d, 0xdf, 0x47, 0x3b, 0xb0, 0x5c, 0x60, 0x84, 0x4e, 0x78, 0x9d, 0x87, 0x1a, 0xb6, + 0x98, 0xc1, 0xcc, 0x35, 0xa0, 0xce, 0x7f, 0x2c, 0x58, 0xfe, 0x20, 0xe0, 0x1d, 0x1c, 0x1c, 0x05, + 0x58, 0xf6, 0x74, 0xcd, 0x18, 0xea, 0xfc, 0x15, 0x34, 0x2d, 0xd6, 0xc6, 0xbd, 0x89, 0xf3, 0x57, + 0xab, 0x99, 0xf6, 0xf1, 0x1e, 0xac, 0xe4, 0xe5, 0x33, 0xcf, 0x37, 0xb3, 0x9b, 0xfd, 0xd5, 0xe7, + 0x5f, 0x6d, 0x2d, 0x65, 0xb9, 0xdd, 0x36, 0xb9, 0x77, 0xe0, 0x2e, 0x91, 0x11, 0x81, 0x8f, 0x36, + 0xa1, 0xc1, 0x3a, 0xc4, 0x93, 0xf4, 0xa9, 0x17, 0x0e, 0xfa, 0x26, 0x55, 0xab, 0x6e, 0x9d, 0x75, + 0xc8, 0x11, 0x7d, 0xfa, 0x70, 0xd0, 0x47, 0xef, 0xc0, 0xeb, 0xd9, 0xe4, 0xe4, 0xc5, 0x38, 0xf0, + 0xb4, 0xbe, 0x3e, 0x0e, 0x61, 0xb2, 0x77, 0xde, 0x5d, 0xcd, 0x56, 0x1f, 0xe3, 0x40, 0x1b, 0xdb, + 0xf3, 0x7d, 0xe1, 0xfc, 0x7d, 0x16, 0x66, 0x0f, 0xb1, 0xc0, 0x7d, 0x89, 0x8e, 0x61, 0x49, 0xd1, + 0x7e, 0x14, 0x60, 0x45, 0xbd, 0xa4, 0x35, 0xa7, 0x3b, 0xbd, 0x66, 0x5a, 0x76, 0x79, 0xca, 0x69, + 0x96, 0xe6, 0x9a, 0x78, 0xb7, 0xd9, 0x36, 0xd2, 0x23, 0x85, 0x15, 0x75, 0x17, 0x33, 0x8e, 0x44, + 0x88, 0x6e, 0x82, 0xad, 0xc4, 0x40, 0xaa, 0xa2, 0x69, 0x16, 0xdd, 0x22, 0x89, 0xe5, 0xeb, 0xd9, + 0x7a, 0xd2, 0x67, 0xf2, 0x2e, 0x71, 0x7e, 0x7f, 0xac, 0x7c, 0x93, 0xfe, 0x78, 0x04, 0xab, 0x7a, + 0xb8, 0x18, 0xe7, 0xac, 0x4e, 0xce, 0xb9, 0xa2, 0xf5, 0x47, 0x49, 0x3f, 0x02, 0x14, 0x4b, 0x32, + 0xce, 0x39, 0xf3, 0x0a, 0x7e, 0xc6, 0x92, 0x8c, 0x52, 0xfa, 0xb0, 0x21, 0x75, 0xf2, 0x79, 0x7d, + 0xaa, 0x4c, 0xb7, 0x8d, 0x02, 0x1a, 0x32, 0xd9, 0xcb, 0xc8, 0x67, 0x27, 0x27, 0x5f, 0x37, 0x44, + 0x0f, 0x34, 0x8f, 0x9b, 0xd1, 0xa4, 0x56, 0xda, 0xb0, 0x79, 0xbe, 0x95, 0x3c, 0x40, 0x73, 0x26, + 0x40, 0xdf, 0x39, 0x87, 0x22, 0x8f, 0x92, 0x84, 0x37, 0x4b, 0x53, 0x81, 0xbe, 0xd5, 0x9e, 0xb9, + 0x50, 0x9e, 0xa0, 0x5d, 0xdd, 0x3a, 0x71, 0x32, 0x20, 0x50, 0x9a, 0x4f, 0x36, 0x69, 0xf5, 0xd0, + 0xb3, 0x6b, 0x5e, 0x39, 0xda, 0x9c, 0x85, 0xe9, 0xf8, 0xe7, 0x14, 0xc3, 0x43, 0x5e, 0x23, 0xdc, + 0x12, 0xd7, 0xfb, 0x94, 0xea, 0xdb, 0x5c, 0x1a, 0x20, 0x68, 0xc4, 0x49, 0xcf, 0x0c, 0x38, 0x15, + 0x77, 0x31, 0x1f, 0x16, 0x6e, 0x6b, 0x29, 0xfa, 0x18, 0xae, 0x85, 0x83, 0x7e, 0x87, 0x0a, 0x8f, + 0x9f, 0x24, 0x40, 0x53, 0x01, 0xa4, 0xc2, 0x42, 0x79, 0x82, 0x12, 0xca, 0x62, 0x9d, 0x99, 0x89, + 0xe7, 0xd2, 0xcc, 0x2f, 0x15, 0xf7, 0x6a, 0xa2, 0xf2, 0xe8, 0xc4, 0x70, 0xc8, 0x63, 0x7e, 0xa4, + 0xe1, 0x6e, 0x86, 0x4e, 0x1c, 0x93, 0xf7, 0xaa, 0xb5, 0xda, 0x72, 0xdd, 0xf9, 0x01, 0xd4, 0x4d, + 0xa1, 0xd8, 0x23, 0xa7, 0xd2, 0x54, 0x6f, 0xdf, 0x17, 0x54, 0x4a, 0x2a, 0x6d, 0x2b, 0xad, 0xde, + 0x99, 0xc0, 0x51, 0xb0, 0x7e, 0xd1, 0x08, 0x2e, 0xd1, 0x13, 0x98, 0x8b, 0xa8, 0x99, 0x0f, 0x8d, + 0x62, 0xe3, 0xc6, 0xbb, 0xcd, 0x09, 0x7e, 0x20, 0x35, 0x2f, 0x22, 0x74, 0x33, 0x36, 0x47, 0x14, + 0x83, 0xff, 0x58, 0xa7, 0x97, 0xe8, 0xf1, 0xb8, 0xd1, 0x9f, 0xbc, 0x92, 0xd1, 0x31, 0xbe, 0xc2, + 0xe6, 0x35, 0x68, 0xec, 0x25, 0xdb, 0xfe, 0x50, 0xb7, 0xad, 0x33, 0xc7, 0x32, 0x5f, 0x3e, 0x96, + 0x7b, 0xb0, 0x98, 0x4e, 0x53, 0xc7, 0xdc, 0x14, 0x3b, 0xf4, 0x5d, 0x80, 0x74, 0x0c, 0xd3, 0x45, + 0x32, 0x69, 0x07, 0xf5, 0x54, 0x72, 0xd7, 0x1f, 0xe9, 0xd8, 0xd3, 0x23, 0x1d, 0xdb, 0x71, 0x61, + 0xe9, 0xb1, 0x24, 0x3f, 0xcd, 0x46, 0xed, 0x47, 0x91, 0x44, 0xaf, 0xc1, 0xac, 0xbe, 0x9f, 0x29, + 0x51, 0xd5, 0x9d, 0x89, 0x25, 0xb9, 0x6b, 0x3a, 0x42, 0x31, 0xce, 0xf3, 0xc8, 0x63, 0xbe, 0xb4, + 0xa7, 0xb7, 0x2b, 0x3b, 0x55, 0x77, 0x71, 0x50, 0xa8, 0xdf, 0xf5, 0xa5, 0xf3, 0x33, 0x68, 0x94, + 0x08, 0xd1, 0x22, 0x4c, 0xe7, 0x5c, 0xd3, 0xcc, 0x47, 0xb7, 0x60, 0xbd, 0x20, 0x1a, 0x2d, 0xf1, + 0x09, 0x63, 0xdd, 0xbd, 0x94, 0x03, 0x46, 0xaa, 0xbc, 0x74, 0x1e, 0xc1, 0xda, 0xdd, 0xa2, 0xa0, + 0xe4, 0x0d, 0x64, 0x64, 0x87, 0xd6, 0xe8, 0x4c, 0xb2, 0x01, 0xf5, 0xfc, 0x67, 0xac, 0xd9, 0x7d, + 0xd5, 0x2d, 0x04, 0x4e, 0x1f, 0x96, 0x1f, 0x4b, 0x72, 0x44, 0x43, 0xbf, 0x20, 0xbb, 0xe0, 0x00, + 0xf6, 0xc7, 0x89, 0x26, 0xfe, 0x4d, 0x54, 0x98, 0xe3, 0xb0, 0xfe, 0xb8, 0x3c, 0xc0, 0x98, 0xe6, + 0x7e, 0x88, 0xc9, 0x29, 0x55, 0x12, 0xb9, 0x50, 0x35, 0x83, 0x4a, 0x92, 0x59, 0x37, 0x2f, 0xcc, + 0xac, 0x78, 0xb7, 0x79, 0x11, 0xc9, 0x01, 0x56, 0x38, 0xad, 0x0b, 0x86, 0xcb, 0xf9, 0x3e, 0xac, + 0x3e, 0xc0, 0x6a, 0x20, 0xa8, 0x3f, 0x12, 0xe3, 0x65, 0xa8, 0xe8, 0xf8, 0x59, 0x26, 0x7e, 0xfa, + 0x51, 0xcf, 0x1a, 0xf6, 0xed, 0x4f, 0x22, 0x2e, 0x14, 0xf5, 0xcf, 0x9c, 0xc8, 0x4b, 0x8e, 0xf7, + 0x14, 0x56, 0xf5, 0x61, 0x49, 0x1a, 0xfa, 0x5e, 0xbe, 0xcf, 0x24, 0x8e, 0x8d, 0x1b, 0x3f, 0x9e, + 0xe8, 0x76, 0x8c, 0x9b, 0x4b, 0x37, 0xb0, 0x12, 0x8f, 0xc9, 0xa5, 0xf3, 0x5b, 0x0b, 0xec, 0xfb, + 0x74, 0xb8, 0x27, 0x25, 0xeb, 0x86, 0x7d, 0x1a, 0x2a, 0x5d, 0x5f, 0x31, 0xa1, 0xfa, 0x11, 0xbd, + 0x01, 0x0b, 0x79, 0x3f, 0x37, 0x6d, 0xdc, 0x32, 0x6d, 0x7c, 0x3e, 0x13, 0xea, 0x0b, 0x86, 0x6e, + 0x01, 0x44, 0x82, 0xc6, 0x1e, 0xf1, 0x4e, 0xe9, 0x30, 0x8d, 0xe2, 0x46, 0xb9, 0x3d, 0x27, 0x1f, + 0x19, 0x9a, 0x87, 0x83, 0x4e, 0xc0, 0xc8, 0x7d, 0x3a, 0x74, 0x6b, 0x1a, 0xdf, 0xbe, 0x4f, 0x87, + 0x7a, 0xde, 0x32, 0xd3, 0xab, 0xe9, 0xa9, 0x15, 0x37, 0x79, 0x71, 0x7e, 0x67, 0xc1, 0xa5, 0x3c, + 0x1c, 0x59, 0xba, 0x1e, 0x0e, 0x3a, 0x5a, 0xe3, 0x25, 0xe7, 0x76, 0xc6, 0xdb, 0xe9, 0x73, 0xbc, + 0x7d, 0x0f, 0xe6, 0xf3, 0x0b, 0xa2, 0xfd, 0xad, 0x4c, 0xe0, 0x6f, 0x23, 0xd3, 0xb8, 0x4f, 0x87, + 0xce, 0x2f, 0x4b, 0xbe, 0xed, 0x0f, 0x4b, 0xb5, 0x4f, 0xfc, 0x0f, 0xdf, 0x72, 0xb3, 0x65, 0xdf, + 0x48, 0x59, 0xff, 0xcc, 0x06, 0x2a, 0x67, 0x37, 0xe0, 0xfc, 0xd1, 0x82, 0xb5, 0xb2, 0x55, 0x79, + 0xcc, 0x0f, 0xc5, 0x20, 0xa4, 0x2f, 0xb3, 0x5e, 0x5c, 0xbf, 0xe9, 0xf2, 0xf5, 0x7b, 0x02, 0x8b, + 0x23, 0x4e, 0xc9, 0xf4, 0x34, 0xde, 0x9e, 0x28, 0xc7, 0x4a, 0xd5, 0xd5, 0x5d, 0x28, 0xef, 0x43, + 0x3a, 0xff, 0xb0, 0x60, 0x25, 0xf3, 0x31, 0x3f, 0x2c, 0xf4, 0x43, 0x40, 0xf9, 0xf6, 0x8a, 0xc9, + 0x30, 0x49, 0xa9, 0xe5, 0x6c, 0x25, 0x1b, 0x0b, 0x8b, 0xd4, 0x98, 0x2e, 0xa5, 0x06, 0xfa, 0x10, + 0x56, 0x73, 0x97, 0x23, 0x13, 0xa0, 0x89, 0xa3, 0x98, 0xcf, 0xbe, 0xb9, 0x08, 0x6d, 0x41, 0xe3, + 0x17, 0x9c, 0x85, 0xe5, 0xcf, 0x43, 0x15, 0x17, 0xb4, 0x28, 0xf9, 0xf2, 0xe3, 0xfc, 0xc6, 0x2a, + 0xfa, 0x65, 0xda, 0x73, 0xf7, 0x82, 0x20, 0xfd, 0x45, 0x81, 0x22, 0x98, 0xcb, 0xba, 0x76, 0x52, + 0x60, 0x36, 0xce, 0x9d, 0x2c, 0x0e, 0x28, 0x31, 0xc3, 0xc5, 0x4d, 0x7d, 0x07, 0xff, 0xf2, 0xf5, + 0xd6, 0xb5, 0x2e, 0x53, 0xbd, 0x41, 0xa7, 0x49, 0x78, 0xbf, 0x95, 0x7e, 0x45, 0x4b, 0xfe, 0x5d, + 0x97, 0xfe, 0x69, 0x4b, 0x0d, 0x23, 0x2a, 0x33, 0x1d, 0xf9, 0xe7, 0x7f, 0xff, 0xed, 0x2d, 0xcb, + 0xcd, 0xcc, 0xec, 0x3f, 0xf9, 0xe2, 0xf9, 0xa6, 0xf5, 0xe5, 0xf3, 0x4d, 0xeb, 0x5f, 0xcf, 0x37, + 0xad, 0x4f, 0x5f, 0x6c, 0x4e, 0x7d, 0xf9, 0x62, 0x73, 0xea, 0x9f, 0x2f, 0x36, 0xa7, 0x3e, 0x7e, + 0xf7, 0x2c, 0x69, 0x11, 0xc4, 0xeb, 0xf9, 0x77, 0xcb, 0xf8, 0x47, 0xad, 0x4f, 0x46, 0xbf, 0x8a, + 0x1a, 0x7b, 0x9d, 0x59, 0x53, 0x6e, 0xdf, 0xf9, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd1, 0x49, + 0x1e, 0xe4, 0x46, 0x15, 0x00, 0x00, } func (m *ConsumerAdditionProposal) Marshal() (dAtA []byte, err error) { @@ -2275,6 +2300,11 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.NumberOfEpochsToStartReceivingRewards != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.NumberOfEpochsToStartReceivingRewards)) + i-- + dAtA[i] = 0x58 + } if m.BlocksPerEpoch != 0 { i = encodeVarintProvider(dAtA, i, uint64(m.BlocksPerEpoch)) i-- @@ -3009,6 +3039,11 @@ func (m *ConsumerValidator) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.JoinHeight != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.JoinHeight)) + i-- + dAtA[i] = 0x20 + } if m.ConsumerPublicKey != nil { { size, err := m.ConsumerPublicKey.MarshalToSizedBuffer(dAtA[:i]) @@ -3328,6 +3363,9 @@ func (m *Params) Size() (n int) { if m.BlocksPerEpoch != 0 { n += 1 + sovProvider(uint64(m.BlocksPerEpoch)) } + if m.NumberOfEpochsToStartReceivingRewards != 0 { + n += 1 + sovProvider(uint64(m.NumberOfEpochsToStartReceivingRewards)) + } return n } @@ -3624,6 +3662,9 @@ func (m *ConsumerValidator) Size() (n int) { l = m.ConsumerPublicKey.Size() n += 1 + l + sovProvider(uint64(l)) } + if m.JoinHeight != 0 { + n += 1 + sovProvider(uint64(m.JoinHeight)) + } return n } @@ -5503,6 +5544,25 @@ func (m *Params) Unmarshal(dAtA []byte) error { break } } + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NumberOfEpochsToStartReceivingRewards", wireType) + } + m.NumberOfEpochsToStartReceivingRewards = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NumberOfEpochsToStartReceivingRewards |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipProvider(dAtA[iNdEx:]) @@ -7441,6 +7501,25 @@ func (m *ConsumerValidator) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field JoinHeight", wireType) + } + m.JoinHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.JoinHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipProvider(dAtA[iNdEx:]) From 84b55448bf04791a3eb867c18d18c8875969259a Mon Sep 17 00:00:00 2001 From: Marius Poke Date: Thu, 20 Jun 2024 13:28:05 +0200 Subject: [PATCH 070/102] chore: add bots for v4.3.0 (#1975) add bots for v4.3.0 --- .github/dependabot.yml | 20 ++++++++++++++++++++ .mergify.yml | 17 +++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index cb2c06d1a9..61976cf2f3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -38,6 +38,26 @@ updates: labels: - dependencies + - package-ecosystem: gomod + directory: "/" + schedule: + interval: daily + target-branch: "release/v4.3.x" + # Only allow automated security-related dependency updates on release branches. + open-pull-requests-limit: 0 + labels: + - dependencies + + - package-ecosystem: gomod + directory: "/" + schedule: + interval: daily + target-branch: "release/v4.3.x-lsm" + # Only allow automated security-related dependency updates on release branches. + open-pull-requests-limit: 0 + labels: + - dependencies + - package-ecosystem: gomod directory: "/" schedule: diff --git a/.mergify.yml b/.mergify.yml index 6344fe199a..c5825b5bf8 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -26,6 +26,23 @@ pull_request_rules: backport: branches: - release/v4.2.x-lsm +pull_request_rules: + - name: Backport patches to the release/v4.3.x branch + conditions: + - base=main + - label=A:backport/v4.3.x + actions: + backport: + branches: + - release/v4.3.x + - name: Backport patches to the release/v4.3.x-lsm branch + conditions: + - base=main + - label=A:backport/v4.3.x-lsm + actions: + backport: + branches: + - release/v4.3.x-lsm - name: Backport patches to the release/v5.x branch conditions: - base=main From 1c090212206a4b89719472330234722ffc690e0c Mon Sep 17 00:00:00 2001 From: MSalopek Date: Thu, 20 Jun 2024 13:28:12 +0200 Subject: [PATCH 071/102] chore: bump ibc-go to v7.6.0 (#1974) * deps!: bump ibc-go to v7.6.0 * docs: update changelog files --- .changelog/unreleased/dependencies/1924-bump-ibc.md | 3 --- .changelog/unreleased/dependencies/1974-bump-ibc.md | 3 +++ .changelog/unreleased/state-breaking/1974-bump-ibc.md | 3 +++ go.mod | 4 ++-- go.sum | 8 ++++---- 5 files changed, 12 insertions(+), 9 deletions(-) delete mode 100644 .changelog/unreleased/dependencies/1924-bump-ibc.md create mode 100644 .changelog/unreleased/dependencies/1974-bump-ibc.md create mode 100644 .changelog/unreleased/state-breaking/1974-bump-ibc.md diff --git a/.changelog/unreleased/dependencies/1924-bump-ibc.md b/.changelog/unreleased/dependencies/1924-bump-ibc.md deleted file mode 100644 index 8980dd3b9a..0000000000 --- a/.changelog/unreleased/dependencies/1924-bump-ibc.md +++ /dev/null @@ -1,3 +0,0 @@ -- Bump [ibc-go](https://github.com/cosmos/ibc-go) to - [v7.5.1](https://github.com/cosmos/ibc-go/releases/tag/v7.5.1). - ([\#1924](https://github.com/cosmos/interchain-security/pull/1924)) \ No newline at end of file diff --git a/.changelog/unreleased/dependencies/1974-bump-ibc.md b/.changelog/unreleased/dependencies/1974-bump-ibc.md new file mode 100644 index 0000000000..f8b953459e --- /dev/null +++ b/.changelog/unreleased/dependencies/1974-bump-ibc.md @@ -0,0 +1,3 @@ +- Bump [ibc-go](https://github.com/cosmos/ibc-go) to + [v7.6.0](https://github.com/cosmos/ibc-go/releases/tag/v7.6.0). + ([\#1974](https://github.com/cosmos/interchain-security/pull/1974)) \ No newline at end of file diff --git a/.changelog/unreleased/state-breaking/1974-bump-ibc.md b/.changelog/unreleased/state-breaking/1974-bump-ibc.md new file mode 100644 index 0000000000..f8b953459e --- /dev/null +++ b/.changelog/unreleased/state-breaking/1974-bump-ibc.md @@ -0,0 +1,3 @@ +- Bump [ibc-go](https://github.com/cosmos/ibc-go) to + [v7.6.0](https://github.com/cosmos/ibc-go/releases/tag/v7.6.0). + ([\#1974](https://github.com/cosmos/interchain-security/pull/1974)) \ No newline at end of file diff --git a/go.mod b/go.mod index 4ec104c1fa..c9512cbf61 100644 --- a/go.mod +++ b/go.mod @@ -7,9 +7,9 @@ require ( cosmossdk.io/math v1.3.0 github.com/cometbft/cometbft v0.37.6 github.com/cometbft/cometbft-db v0.11.0 - github.com/cosmos/cosmos-sdk v0.47.11 + github.com/cosmos/cosmos-sdk v0.47.12 github.com/cosmos/gogoproto v1.4.10 - github.com/cosmos/ibc-go/v7 v7.5.1 + github.com/cosmos/ibc-go/v7 v7.6.0 github.com/cosmos/ics23/go v0.10.0 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.4 diff --git a/go.sum b/go.sum index cfa6b7926d..f32f489b19 100644 --- a/go.sum +++ b/go.sum @@ -335,8 +335,8 @@ github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= github.com/cosmos/cosmos-proto v1.0.0-beta.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.47.11 h1:0Qx7eORw0RJqPv+mvDuU8NQ1LV3nJJKJnPoYblWHolc= -github.com/cosmos/cosmos-sdk v0.47.11/go.mod h1:ADjORYzUQqQv/FxDi0H0K5gW/rAk1CiDR3ZKsExfJV0= +github.com/cosmos/cosmos-sdk v0.47.12 h1:KOZHAVWrcilHywBN/FabBaXbDFMzoFmtdX0hqy5Ory8= +github.com/cosmos/cosmos-sdk v0.47.12/go.mod h1:ADjORYzUQqQv/FxDi0H0K5gW/rAk1CiDR3ZKsExfJV0= 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= @@ -347,8 +347,8 @@ github.com/cosmos/gogoproto v1.4.10 h1:QH/yT8X+c0F4ZDacDv3z+xE3WU1P1Z3wQoLMBRJoK github.com/cosmos/gogoproto v1.4.10/go.mod h1:3aAZzeRWpAwr+SS/LLkICX2/kDFyaYVzckBDzygIxek= github.com/cosmos/iavl v0.20.1 h1:rM1kqeG3/HBT85vsZdoSNsehciqUQPWrR4BYmqE2+zg= github.com/cosmos/iavl v0.20.1/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= -github.com/cosmos/ibc-go/v7 v7.5.1 h1:KqS/g7W7EMX1OtOvufS8lWMJibOKpdgtNNZIU6fAgVU= -github.com/cosmos/ibc-go/v7 v7.5.1/go.mod h1:ktFg5GvKOyrGCqTWtW7Grj5uweU4ZapxrNeVS1CLLbo= +github.com/cosmos/ibc-go/v7 v7.6.0 h1:S1G5hcIVe9go+jQV6F9+I9yy+hylbJeLiVHUmktQNrM= +github.com/cosmos/ibc-go/v7 v7.6.0/go.mod h1:LifBA7JHRHl95ujjHIaBEHmUqy2qCGyqDCXB7qmAsZk= 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.12.4 h1:drvWt+GJP7Aiw550yeb3ON/zsrgW0jgh5saFCr7pDnw= From 060c943b2f17b2696e668c669352a366d3610d5d Mon Sep 17 00:00:00 2001 From: MSalopek Date: Thu, 20 Jun 2024 14:02:18 +0200 Subject: [PATCH 072/102] docs: add docs section to RELEASE_PROCESS.md (#1976) * docs: add docs section to RELEASE_PROCESS.md * docs: update the release notes template --- RELEASE_NOTES.md | 2 +- RELEASE_PROCESS.md | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 3a172a79c4..ce576b63cf 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -17,7 +17,7 @@ ** REMOVE THE LINE BELOW TO ENABLE THE MARKDOWN LINK CHECKER FOR RELEASE ** -Check out the [changelog](https://github.com/cosmos/interchain-security/blob//CHANGELOG.md) for a list of relevant changes or [compare all changes](https://github.com/cosmos/interchain-security/compare/release/...) from last release. +Check out the [changelog](https://github.com/cosmos/interchain-security/blob//CHANGELOG.md) for a list of relevant changes or [compare all changes](https://github.com/cosmos/interchain-security/compare/...) from last release. Refer to the [upgrading guide](https://github.com/cosmos/interchain-security/blob/release//UPGRADING.md) when migrating from `` to ``. diff --git a/RELEASE_PROCESS.md b/RELEASE_PROCESS.md index 04d7731a72..e1c74d428b 100644 --- a/RELEASE_PROCESS.md +++ b/RELEASE_PROCESS.md @@ -107,6 +107,78 @@ Once the **final release** is cut, the new changelog section must be added to ma ``` - open a PR (from this new created branch) against `main` +## Updating published docs + +### Before tagging a new release + +On your release branch, clear the `docs/versions.json` file so it looks like this: +```json +[] +``` + +If this file is populated on a release branch it will cause the tag to have extra files that the docs deploy process does not expect. This could cause the deploy process to fail in some situations. + +### After tagging a new release + +Go to `main` branch and update the `docs/versions.json` to include all the versions you want to publish on the docs page: +```json +[ + "v4.3.0", + "v5.0.0" +] +``` + +This will cause the docs to be built with `main`, `v4.3.0` and `v5.0.0`. + +In `docs/docusaurus.config.js` change the `preset` section to display the versions you chose in `docs/versions.json`. + +For example, here we remove v4.2.0 and replace it with v4.3.0. + +```diff + presets: [ + [ + "classic", + /** @type {import('@docusaurus/preset-classic').Options} */ + ({ + docs: { + sidebarPath: require.resolve("./sidebars.js"), + routeBasePath: "/", + versions: { + current: { + path: "/", + label: "main", + banner: "unreleased", + }, + // v4.2.0-docs was a special tags for docs + // this is not usually necessary +- "v4.2.0-docs": { +- path: "/v4.2.0/", +- label: "v4.2.0", +- banner: "none", +- }, ++ "v4.3.0": { ++ banner: "none", ++ }, + "v5.0.0": { + banner: "unreleased", + }, + }, + remarkPlugins: [remarkMath], + rehypePlugins: [rehypeKatex], + }, + + theme: { + customCss: require.resolve("./src/css/custom.css"), + }, + }), + ], + ], +``` + +The documentation is updated automatically whenever `main` is changed. + +To learn how to work with docs locally check the docs [README.md on main branch](https://github.com/cosmos/interchain-security/blob/main/docs/README.md) + ## Tagging Procedure **Important**: _**Always create tags from your local machine**_ since all release From 59bfdc3a5a4ad9ed94b57c64ff1202d929915ad4 Mon Sep 17 00:00:00 2001 From: Marius Poke Date: Mon, 24 Jun 2024 09:31:10 +0200 Subject: [PATCH 073/102] docs: bring v4.3.0 changelog to main (#1980) * bring v4.3.0 changelog to main * fix release notes template --- .../provider/1925-apply-audit-suggestions.md | 3 -- .../bug-fixes/1921-write-stderr.md | 0 .../provider/1925-apply-audit-suggestions.md | 3 ++ .../provider/1946-get-consumer-chains.md | 0 .../dependencies/1974-bump-ibc.md | 0 .../provider/1932-allow-pss-params-changes.md | 0 ...ards-to-long-term-validating-validators.md | 0 .../state-breaking/1974-bump-ibc.md | 0 .../provider/1925-apply-audit-suggestions.md | 0 ...ards-to-long-term-validating-validators.md | 0 .../provider/1932-allow-pss-params-changes.md | 0 .changelog/v4.3.0/summary.md | 1 + CHANGELOG.md | 51 +++++++++++++++++++ RELEASE_NOTES.md | 2 +- 14 files changed, 56 insertions(+), 4 deletions(-) delete mode 100644 .changelog/unreleased/bug-fixes/provider/1925-apply-audit-suggestions.md rename .changelog/{unreleased => v4.3.0}/bug-fixes/1921-write-stderr.md (100%) create mode 100644 .changelog/v4.3.0/bug-fixes/provider/1925-apply-audit-suggestions.md rename .changelog/{unreleased => v4.3.0}/bug-fixes/provider/1946-get-consumer-chains.md (100%) rename .changelog/{unreleased => v4.3.0}/dependencies/1974-bump-ibc.md (100%) rename .changelog/{unreleased => v4.3.0}/features/provider/1932-allow-pss-params-changes.md (100%) rename .changelog/{unreleased => v4.3.0}/improvements/provider/1929-distribute-rewards-to-long-term-validating-validators.md (100%) rename .changelog/{unreleased => v4.3.0}/state-breaking/1974-bump-ibc.md (100%) rename .changelog/{unreleased => v4.3.0}/state-breaking/provider/1925-apply-audit-suggestions.md (100%) rename .changelog/{unreleased => v4.3.0}/state-breaking/provider/1929-distribute-rewards-to-long-term-validating-validators.md (100%) rename .changelog/{unreleased => v4.3.0}/state-breaking/provider/1932-allow-pss-params-changes.md (100%) create mode 100644 .changelog/v4.3.0/summary.md diff --git a/.changelog/unreleased/bug-fixes/provider/1925-apply-audit-suggestions.md b/.changelog/unreleased/bug-fixes/provider/1925-apply-audit-suggestions.md deleted file mode 100644 index 3d12033a4e..0000000000 --- a/.changelog/unreleased/bug-fixes/provider/1925-apply-audit-suggestions.md +++ /dev/null @@ -1,3 +0,0 @@ -- Apply audit suggestions that include a bug fix in the way we compute the - maximum capped power. ([\#1925](https://github.com/cosmos/interchain- - security/pull/1925)) diff --git a/.changelog/unreleased/bug-fixes/1921-write-stderr.md b/.changelog/v4.3.0/bug-fixes/1921-write-stderr.md similarity index 100% rename from .changelog/unreleased/bug-fixes/1921-write-stderr.md rename to .changelog/v4.3.0/bug-fixes/1921-write-stderr.md diff --git a/.changelog/v4.3.0/bug-fixes/provider/1925-apply-audit-suggestions.md b/.changelog/v4.3.0/bug-fixes/provider/1925-apply-audit-suggestions.md new file mode 100644 index 0000000000..5c9d04c2e7 --- /dev/null +++ b/.changelog/v4.3.0/bug-fixes/provider/1925-apply-audit-suggestions.md @@ -0,0 +1,3 @@ +- Apply audit suggestions that include a bug fix in the way we compute the + maximum capped power. + ([\#1925](https://github.com/cosmos/interchain-security/pull/1925)) diff --git a/.changelog/unreleased/bug-fixes/provider/1946-get-consumer-chains.md b/.changelog/v4.3.0/bug-fixes/provider/1946-get-consumer-chains.md similarity index 100% rename from .changelog/unreleased/bug-fixes/provider/1946-get-consumer-chains.md rename to .changelog/v4.3.0/bug-fixes/provider/1946-get-consumer-chains.md diff --git a/.changelog/unreleased/dependencies/1974-bump-ibc.md b/.changelog/v4.3.0/dependencies/1974-bump-ibc.md similarity index 100% rename from .changelog/unreleased/dependencies/1974-bump-ibc.md rename to .changelog/v4.3.0/dependencies/1974-bump-ibc.md diff --git a/.changelog/unreleased/features/provider/1932-allow-pss-params-changes.md b/.changelog/v4.3.0/features/provider/1932-allow-pss-params-changes.md similarity index 100% rename from .changelog/unreleased/features/provider/1932-allow-pss-params-changes.md rename to .changelog/v4.3.0/features/provider/1932-allow-pss-params-changes.md diff --git a/.changelog/unreleased/improvements/provider/1929-distribute-rewards-to-long-term-validating-validators.md b/.changelog/v4.3.0/improvements/provider/1929-distribute-rewards-to-long-term-validating-validators.md similarity index 100% rename from .changelog/unreleased/improvements/provider/1929-distribute-rewards-to-long-term-validating-validators.md rename to .changelog/v4.3.0/improvements/provider/1929-distribute-rewards-to-long-term-validating-validators.md diff --git a/.changelog/unreleased/state-breaking/1974-bump-ibc.md b/.changelog/v4.3.0/state-breaking/1974-bump-ibc.md similarity index 100% rename from .changelog/unreleased/state-breaking/1974-bump-ibc.md rename to .changelog/v4.3.0/state-breaking/1974-bump-ibc.md diff --git a/.changelog/unreleased/state-breaking/provider/1925-apply-audit-suggestions.md b/.changelog/v4.3.0/state-breaking/provider/1925-apply-audit-suggestions.md similarity index 100% rename from .changelog/unreleased/state-breaking/provider/1925-apply-audit-suggestions.md rename to .changelog/v4.3.0/state-breaking/provider/1925-apply-audit-suggestions.md diff --git a/.changelog/unreleased/state-breaking/provider/1929-distribute-rewards-to-long-term-validating-validators.md b/.changelog/v4.3.0/state-breaking/provider/1929-distribute-rewards-to-long-term-validating-validators.md similarity index 100% rename from .changelog/unreleased/state-breaking/provider/1929-distribute-rewards-to-long-term-validating-validators.md rename to .changelog/v4.3.0/state-breaking/provider/1929-distribute-rewards-to-long-term-validating-validators.md diff --git a/.changelog/unreleased/state-breaking/provider/1932-allow-pss-params-changes.md b/.changelog/v4.3.0/state-breaking/provider/1932-allow-pss-params-changes.md similarity index 100% rename from .changelog/unreleased/state-breaking/provider/1932-allow-pss-params-changes.md rename to .changelog/v4.3.0/state-breaking/provider/1932-allow-pss-params-changes.md diff --git a/.changelog/v4.3.0/summary.md b/.changelog/v4.3.0/summary.md new file mode 100644 index 0000000000..f633d1635e --- /dev/null +++ b/.changelog/v4.3.0/summary.md @@ -0,0 +1 @@ +*June 20, 2024* diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f8fd579ab..6d2c94a34e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,56 @@ # CHANGELOG +## v4.3.0 + +*June 20, 2024* + +### BUG FIXES + +- General + - Write unbonding period advisory to stderr instead of stdout + ([\#1921](https://github.com/cosmos/interchain-security/pull/1921)) +- [Provider](x/ccv/provider) + - Apply audit suggestions that include a bug fix in the way we compute the + maximum capped power. + ([\#1925](https://github.com/cosmos/interchain-security/pull/1925)) + - Replace `GetAllConsumerChains` with lightweight version + (`GetAllRegisteredConsumerChainIDs`) that doesn't call into the staking module + ([\#1946](https://github.com/cosmos/interchain-security/pull/1946)) + +### DEPENDENCIES + +- Bump [ibc-go](https://github.com/cosmos/ibc-go) to + [v7.6.0](https://github.com/cosmos/ibc-go/releases/tag/v7.6.0). + ([\#1974](https://github.com/cosmos/interchain-security/pull/1974)) + +### FEATURES + +- [Provider](x/ccv/provider) + - Allow consumer chains to change their PSS parameters. + ([\#1932](https://github.com/cosmos/interchain-security/pull/1932)) + +### IMPROVEMENTS + +- [Provider](x/ccv/provider) + - Only start distributing rewards to validators after they have been validating + for a fixed number of blocks. Introduces the `NumberOfEpochsToStartReceivingRewards` param. + ([\#1929](https://github.com/cosmos/interchain-security/pull/1929)) + +### STATE BREAKING + +- General + - Bump [ibc-go](https://github.com/cosmos/ibc-go) to + [v7.6.0](https://github.com/cosmos/ibc-go/releases/tag/v7.6.0). + ([\#1974](https://github.com/cosmos/interchain-security/pull/1974)) +- [Provider](x/ccv/provider) + - Apply audit suggestions that include a bug fix in the way we compute the + maximum capped power. ([\#1925](https://github.com/cosmos/interchain-security/pull/1925)) + - Only start distributing rewards to validators after they have been validating + for a fixed number of blocks. Introduces the `NumberOfEpochsToStartReceivingRewards` param. + ([\#1929](https://github.com/cosmos/interchain-security/pull/1929)) + - Allow consumer chains to change their PSS parameters. + ([\#1932](https://github.com/cosmos/interchain-security/pull/1932)) + ## v4.2.0 May 17, 2024 diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index ce576b63cf..d4350c895f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,7 +6,7 @@ - the last release branch: --> -# Replicated Security Release Notes +# Interchain Security Release Notes