diff --git a/.changelog/unreleased/api-breaking/provider/2079-inactive-validators.md b/.changelog/unreleased/api-breaking/provider/2079-inactive-validators.md new file mode 100644 index 0000000000..815f730e58 --- /dev/null +++ b/.changelog/unreleased/api-breaking/provider/2079-inactive-validators.md @@ -0,0 +1,15 @@ +- Add the _Inactive Provider Validators_ feature (as per + [ADR-017](https://cosmos.github.io/interchain-security/adrs/adr-017-allowing-inactive-validators)), + which entails the following changes on the provider. + ([\#2079](https://github.com/cosmos/interchain-security/pull/2079)) + + - Add `max_provider_consensus_validators`, a provider module param that sets + the maximum number of validators that will be passed to the provider consensus engine. + - Add `no_valupdates_genutil` and `no_valupdates_staking`, "wrapper" modules around + the Cosmos SDK's native genutil and staking modules. Both modules provide the exact + same functionality as the native modules, except for *not* returning validator set updates + to the provider consensus engine. + - Return the first `max_provider_consensus_validators` validators (sorted by largest amount of stake first) + to the provider consensus engine. + - Use the `max_validators` validators as basis for the validator sets sent to the consumers + (`max_validators` is a staking module param). \ No newline at end of file diff --git a/.changelog/unreleased/api-breaking/provider/2098-vscmatured-removal.md b/.changelog/unreleased/api-breaking/provider/2098-vscmatured-removal.md new file mode 100644 index 0000000000..b5bb3ff5cc --- /dev/null +++ b/.changelog/unreleased/api-breaking/provider/2098-vscmatured-removal.md @@ -0,0 +1,7 @@ +- The removal of `VSCMaturedPackets` entail several API breaking changes. + ([\#2098](https://github.com/cosmos/interchain-security/pull/2098)) + + - Remove the `oldest_unconfirmed_vsc` query -- used to get + the send timestamp of the oldest unconfirmed VSCPacket. + - Deprecate the `init_timeout_period` and `vsc_timeout_period` parameters + from the provider module. diff --git a/.changelog/unreleased/api-breaking/provider/2130-remove-legacy-proposal.md b/.changelog/unreleased/api-breaking/provider/2130-remove-legacy-proposal.md new file mode 100644 index 0000000000..404e134ea5 --- /dev/null +++ b/.changelog/unreleased/api-breaking/provider/2130-remove-legacy-proposal.md @@ -0,0 +1,25 @@ +- Remove support for legacy-proposal to add/modify/remove consumer proposals and change reward denoms + ([\#2130](https://github.com/cosmos/interchain-security/pull/2130)) + To submit a proposal to add/modify/remove a consumer use the following command + ```shell + interchain-security-pd tx gov submit-proposal [proposal-file] + ``` + + Run `interchain-security-pd tx gov draft-proposal` command and select in `other` one of the following + message types to generate a draft proposal json file: + - `/interchain_security.ccv.provider.v1.MsgConsumerAddition` + + - `/interchain_security.ccv.provider.v1.MsgConsumerModification` + + - `/interchain_security.ccv.provider.v1.MsgConsumerRemoval` + + - `/interchain_security.ccv.provider.v1.MsgChangeRewardDenoms` + + This replaces the following command which are not supported anymore: + + ```shell + interchain-security-pd tx gov submit-legacy-proposal consumer-addition [proposal-file] + interchain-security-pd tx gov submit-legacy-proposal consumer-modification [proposal-file] + interchain-security-pd tx gov submit-legacy-proposal consumer-removal [proposal-file] + interchain-security-pd tx gov submit-legacy-proposal change-reward-denoms [proposal-file] + ``` \ No newline at end of file diff --git a/.changelog/unreleased/dependencies/2113-bump-ibc.md b/.changelog/unreleased/dependencies/2113-bump-ibc.md new file mode 100644 index 0000000000..05104f8c46 --- /dev/null +++ b/.changelog/unreleased/dependencies/2113-bump-ibc.md @@ -0,0 +1,3 @@ +- Bump [ibc-go](https://github.com/cosmos/ibc-go) to + [v8.4.0](https://github.com/cosmos/ibc-go/releases/tag/v8.4.0). + ([\#2113](https://github.com/cosmos/interchain-security/pull/2113)) \ No newline at end of file diff --git a/.changelog/unreleased/features/provider/2035-min-stake-max-rank.md b/.changelog/unreleased/features/provider/2035-min-stake-max-rank.md new file mode 100644 index 0000000000..e48d7f299f --- /dev/null +++ b/.changelog/unreleased/features/provider/2035-min-stake-max-rank.md @@ -0,0 +1,3 @@ +- Add `min_stake`, a power shaping configuration parameter that enables consumers to set + the minimum amount of provider stake every validator needs to be eligible to opt-in. + ([\#2035](https://github.com/cosmos/interchain-security/pull/2035)) diff --git a/.changelog/unreleased/features/provider/2066-allow-inactive-vals.md b/.changelog/unreleased/features/provider/2066-allow-inactive-vals.md new file mode 100644 index 0000000000..d5f85f9508 --- /dev/null +++ b/.changelog/unreleased/features/provider/2066-allow-inactive-vals.md @@ -0,0 +1,3 @@ +- Add `allow_inactive_vals`, a power shaping configuration parameter that enables consumers + to specify whether validators outside the active provider validator set are eligible to opt-in. + ([\#2066](https://github.com/cosmos/interchain-security/pull/2066)) \ No newline at end of file diff --git a/.changelog/unreleased/features/provider/2079-inactive-validators.md b/.changelog/unreleased/features/provider/2079-inactive-validators.md new file mode 100644 index 0000000000..815f730e58 --- /dev/null +++ b/.changelog/unreleased/features/provider/2079-inactive-validators.md @@ -0,0 +1,15 @@ +- Add the _Inactive Provider Validators_ feature (as per + [ADR-017](https://cosmos.github.io/interchain-security/adrs/adr-017-allowing-inactive-validators)), + which entails the following changes on the provider. + ([\#2079](https://github.com/cosmos/interchain-security/pull/2079)) + + - Add `max_provider_consensus_validators`, a provider module param that sets + the maximum number of validators that will be passed to the provider consensus engine. + - Add `no_valupdates_genutil` and `no_valupdates_staking`, "wrapper" modules around + the Cosmos SDK's native genutil and staking modules. Both modules provide the exact + same functionality as the native modules, except for *not* returning validator set updates + to the provider consensus engine. + - Return the first `max_provider_consensus_validators` validators (sorted by largest amount of stake first) + to the provider consensus engine. + - Use the `max_validators` validators as basis for the validator sets sent to the consumers + (`max_validators` is a staking module param). \ No newline at end of file diff --git a/.changelog/unreleased/features/provider/2098-vscmatured-removal.md b/.changelog/unreleased/features/provider/2098-vscmatured-removal.md new file mode 100644 index 0000000000..aa370d81d2 --- /dev/null +++ b/.changelog/unreleased/features/provider/2098-vscmatured-removal.md @@ -0,0 +1,8 @@ +- Remove `VSCMaturedPackets` from the provider module, which entails the following + changes to the provider. + ([\#2098](https://github.com/cosmos/interchain-security/pull/2098)) + + - Remove unbonding operations pausing. + - Remove the CCV channel initialization timeout. + - Remove `VSCPackets` timeout. + - Redesign key assignment pruning -- prune old consumer keys after the unbonding period elapses. diff --git a/.changelog/unreleased/features/provider/2106-query-blocks-until-next-epoch.md b/.changelog/unreleased/features/provider/2106-query-blocks-until-next-epoch.md new file mode 100644 index 0000000000..070fa487e9 --- /dev/null +++ b/.changelog/unreleased/features/provider/2106-query-blocks-until-next-epoch.md @@ -0,0 +1,2 @@ +- Add a query to get the blocks until the next epoch begins + ([\#2106](https://github.com/cosmos/interchain-security/pull/2106)) \ No newline at end of file diff --git a/.changelog/unreleased/state-breaking/provider/2035-min-stake-max-rank.md b/.changelog/unreleased/state-breaking/provider/2035-min-stake-max-rank.md new file mode 100644 index 0000000000..e48d7f299f --- /dev/null +++ b/.changelog/unreleased/state-breaking/provider/2035-min-stake-max-rank.md @@ -0,0 +1,3 @@ +- Add `min_stake`, a power shaping configuration parameter that enables consumers to set + the minimum amount of provider stake every validator needs to be eligible to opt-in. + ([\#2035](https://github.com/cosmos/interchain-security/pull/2035)) diff --git a/.changelog/unreleased/state-breaking/provider/2066-allow-inactive-vals.md b/.changelog/unreleased/state-breaking/provider/2066-allow-inactive-vals.md new file mode 100644 index 0000000000..d5f85f9508 --- /dev/null +++ b/.changelog/unreleased/state-breaking/provider/2066-allow-inactive-vals.md @@ -0,0 +1,3 @@ +- Add `allow_inactive_vals`, a power shaping configuration parameter that enables consumers + to specify whether validators outside the active provider validator set are eligible to opt-in. + ([\#2066](https://github.com/cosmos/interchain-security/pull/2066)) \ No newline at end of file diff --git a/.changelog/unreleased/state-breaking/provider/2079-inactive-validators.md b/.changelog/unreleased/state-breaking/provider/2079-inactive-validators.md new file mode 100644 index 0000000000..6760b3071d --- /dev/null +++ b/.changelog/unreleased/state-breaking/provider/2079-inactive-validators.md @@ -0,0 +1,3 @@ +- Add the _Inactive Provider Validators_ feature (as per + [ADR-017](https://cosmos.github.io/interchain-security/adrs/adr-017-allowing-inactive-validators)). + ([\#2079](https://github.com/cosmos/interchain-security/pull/2079)) \ No newline at end of file diff --git a/.changelog/unreleased/state-breaking/provider/2098-vscmatured-removal.md b/.changelog/unreleased/state-breaking/provider/2098-vscmatured-removal.md new file mode 100644 index 0000000000..bc2dc743c2 --- /dev/null +++ b/.changelog/unreleased/state-breaking/provider/2098-vscmatured-removal.md @@ -0,0 +1,2 @@ +- Remove `VSCMaturedPackets` from the provider module. + ([\#2098](https://github.com/cosmos/interchain-security/pull/2098)) diff --git a/.changelog/v5.1.0/api-breaking/1995-deprecate-soft-opt-out.md b/.changelog/v5.1.0/api-breaking/1995-deprecate-soft-opt-out.md new file mode 100644 index 0000000000..bf47d6e7e4 --- /dev/null +++ b/.changelog/v5.1.0/api-breaking/1995-deprecate-soft-opt-out.md @@ -0,0 +1,2 @@ +- Remove soft opt-out feature. ([\#1995](https://github.com/cosmos/interchain-security/pull/1995)) +Backporting of ([\#1964](https://github.com/cosmos/interchain-security/pull/1964)). \ No newline at end of file diff --git a/.changelog/unreleased/api-breaking/provider/1998-change-ux-in-key-assignment.md b/.changelog/v5.1.0/api-breaking/provider/1998-change-ux-in-key-assignment.md similarity index 100% rename from .changelog/unreleased/api-breaking/provider/1998-change-ux-in-key-assignment.md rename to .changelog/v5.1.0/api-breaking/provider/1998-change-ux-in-key-assignment.md diff --git a/.changelog/unreleased/dependencies/2013-bump-comet.md b/.changelog/v5.1.0/dependencies/2013-bump-comet.md similarity index 100% rename from .changelog/unreleased/dependencies/2013-bump-comet.md rename to .changelog/v5.1.0/dependencies/2013-bump-comet.md diff --git a/.changelog/unreleased/dependencies/2053-bump-ibc.md b/.changelog/v5.1.0/dependencies/2053-bump-ibc.md similarity index 100% rename from .changelog/unreleased/dependencies/2053-bump-ibc.md rename to .changelog/v5.1.0/dependencies/2053-bump-ibc.md diff --git a/.changelog/unreleased/dependencies/2053-bump-sdk.md b/.changelog/v5.1.0/dependencies/2053-bump-sdk.md similarity index 100% rename from .changelog/unreleased/dependencies/2053-bump-sdk.md rename to .changelog/v5.1.0/dependencies/2053-bump-sdk.md diff --git a/.changelog/v5.1.0/features/1995-deprecate-soft-opt-out.md b/.changelog/v5.1.0/features/1995-deprecate-soft-opt-out.md new file mode 100644 index 0000000000..6b447d3ed6 --- /dev/null +++ b/.changelog/v5.1.0/features/1995-deprecate-soft-opt-out.md @@ -0,0 +1,2 @@ +- Remove soft opt-out feature. ([\#1995](https://github.com/cosmos/interchain-security/pull/1995)) + Backporting of ([\#1964](https://github.com/cosmos/interchain-security/pull/1964)). \ No newline at end of file diff --git a/.changelog/v5.1.0/state-breaking/1995-deprecate-soft-opt-out.md b/.changelog/v5.1.0/state-breaking/1995-deprecate-soft-opt-out.md new file mode 100644 index 0000000000..6b447d3ed6 --- /dev/null +++ b/.changelog/v5.1.0/state-breaking/1995-deprecate-soft-opt-out.md @@ -0,0 +1,2 @@ +- Remove soft opt-out feature. ([\#1995](https://github.com/cosmos/interchain-security/pull/1995)) + Backporting of ([\#1964](https://github.com/cosmos/interchain-security/pull/1964)). \ No newline at end of file diff --git a/.changelog/unreleased/state-breaking/2013-bump-comet.md b/.changelog/v5.1.0/state-breaking/2013-bump-comet.md similarity index 100% rename from .changelog/unreleased/state-breaking/2013-bump-comet.md rename to .changelog/v5.1.0/state-breaking/2013-bump-comet.md diff --git a/.changelog/unreleased/state-breaking/2053-bump-ibc.md b/.changelog/v5.1.0/state-breaking/2053-bump-ibc.md similarity index 100% rename from .changelog/unreleased/state-breaking/2053-bump-ibc.md rename to .changelog/v5.1.0/state-breaking/2053-bump-ibc.md diff --git a/.changelog/unreleased/state-breaking/2053-bump-sdk.md b/.changelog/v5.1.0/state-breaking/2053-bump-sdk.md similarity index 100% rename from .changelog/unreleased/state-breaking/2053-bump-sdk.md rename to .changelog/v5.1.0/state-breaking/2053-bump-sdk.md diff --git a/.changelog/unreleased/state-breaking/provider/1998-change-ux-in-key-assignment.md b/.changelog/v5.1.0/state-breaking/provider/1998-change-ux-in-key-assignment.md similarity index 100% rename from .changelog/unreleased/state-breaking/provider/1998-change-ux-in-key-assignment.md rename to .changelog/v5.1.0/state-breaking/provider/1998-change-ux-in-key-assignment.md diff --git a/.changelog/v5.1.0/summary.md b/.changelog/v5.1.0/summary.md new file mode 100644 index 0000000000..aea175d241 --- /dev/null +++ b/.changelog/v5.1.0/summary.md @@ -0,0 +1 @@ +*July 19, 2024* diff --git a/.changelog/v5.1.1/api-breaking/provider/2095-proto-message-fix.md b/.changelog/v5.1.1/api-breaking/provider/2095-proto-message-fix.md new file mode 100644 index 0000000000..2fa746a166 --- /dev/null +++ b/.changelog/v5.1.1/api-breaking/provider/2095-proto-message-fix.md @@ -0,0 +1,2 @@ +- Fix incorrect message definitions in the proto files of the provider module + ([\#2095](https://github.com/cosmos/interchain-security/pull/2095)) \ No newline at end of file diff --git a/.changelog/v5.1.1/state-breaking/provider/2095-proto-message-fix.md b/.changelog/v5.1.1/state-breaking/provider/2095-proto-message-fix.md new file mode 100644 index 0000000000..2fa746a166 --- /dev/null +++ b/.changelog/v5.1.1/state-breaking/provider/2095-proto-message-fix.md @@ -0,0 +1,2 @@ +- Fix incorrect message definitions in the proto files of the provider module + ([\#2095](https://github.com/cosmos/interchain-security/pull/2095)) \ No newline at end of file diff --git a/.changelog/v5.1.1/summary.md b/.changelog/v5.1.1/summary.md new file mode 100644 index 0000000000..02e8a8b342 --- /dev/null +++ b/.changelog/v5.1.1/summary.md @@ -0,0 +1 @@ +*July 26, 2024* diff --git a/.coderabbit.yml b/.coderabbit.yml index 7dfe09c9ba..c8978d1f98 100644 --- a/.coderabbit.yml +++ b/.coderabbit.yml @@ -9,6 +9,7 @@ reviews: path_filters: - "!api/" - "!docs/" + - "!*.md" path_instructions: - path: "**/*.go" instructions: "Review the Golang code for conformity with the Uber Golang style guide, highlighting any deviations. Only report issues that you have a high degree of confidence in." @@ -21,14 +22,8 @@ reviews: - path: "**/*_test.go" instructions: | "Assess the unit test code assessing sufficient code coverage for the changes associated in the pull request. Only report issues that you have a high degree of confidence in." - - path: "**/*.md" - instructions: | - "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. Only report issues that you have a high degree of confidence in." - - path: ".changelog/*" - instructions: | - "Assess the changes in the changelog for correctness and completeness, particularly flagging missing changes. Only report issues that you have a high degree of confidence in." auto_review: - enabled: true + enabled: false ignore_title_keywords: - "WIP" - "DO NOT MERGE" diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 8aea8d5a38..90c79f1680 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -42,7 +42,7 @@ jobs: # https://github.com/sigstore/cosign-installer - name: Install cosign if: github.event_name != 'pull_request' - uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0 + uses: sigstore/cosign-installer@4959ce089c160fddf62f7b42464195ba1a56d382 #v3.6.0 with: cosign-release: 'v2.2.3' @@ -50,7 +50,7 @@ jobs: # multi-platform images and export cache # https://github.com/docker/setup-buildx-action - name: Set up Docker Buildx - uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27 # v3.5.0 + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 # Login against a Docker registry except on PR @@ -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@5176d81f87c23d6fc96624dfdbcd9f3830bbe445 #v6.5.0 + uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 #v6.7.0 with: context: . file: ./Dockerfile diff --git a/.github/workflows/gosec.yml b/.github/workflows/gosec.yml index 90f1379eb1..6e23df8031 100644 --- a/.github/workflows/gosec.yml +++ b/.github/workflows/gosec.yml @@ -28,4 +28,4 @@ jobs: - name: Run Gosec Security Scanner uses: securego/gosec@master with: - args: -exclude-dir=tests ./... -exclude-generated ./... + args: -exclude-dir=tests -exclude-dir=app -exclude-generated ./... diff --git a/.github/workflows/nightly-e2e.yml b/.github/workflows/nightly-e2e.yml index 3e6915771e..33a67ec00d 100644 --- a/.github/workflows/nightly-e2e.yml +++ b/.github/workflows/nightly-e2e.yml @@ -36,7 +36,7 @@ jobs: # Run compatibility tests for different consumer (-cv) and provider (-pv) versions. # Combination of all provider versions with consumer versions are tested. # For new versions to be tested add/modify -pc/-cv parameters. - run: go run ./tests/e2e/... --tc compatibility -pv latest -pv v4.3.0-lsm -pv v3.3.3-lsm -cv latest -cv v4.3.0 -cv v3.3.0 + run: go run ./tests/e2e/... --tc compatibility -pv latest -pv v4.3.1-lsm -pv v3.3.3-lsm -cv latest -cv v4.4.0 -cv v3.3.0 happy-path-test: runs-on: ubuntu-latest timeout-minutes: 20 @@ -310,6 +310,23 @@ jobs: - name: E2E active set changes run: go run ./tests/e2e/... --tc active-set-changes + inactive-provider-validators-on-consumer-test: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v5 + with: + go-version: "1.22" + - uses: actions/checkout@v4 + - name: Checkout LFS objects + run: git lfs checkout + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.22" # The Go version to download (if necessary) and use. + - name: E2E inactive provider validators on consumer + run: go run ./tests/e2e/... --tc inactive-provider-validators-on-consumer + nightly-test-fail: needs: - happy-path-test diff --git a/.github/workflows/proto-registry.yml b/.github/workflows/proto-registry.yml index f1f52b55c7..4f817f987c 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.34.0 + - uses: bufbuild/buf-setup-action@v1.38.0 - uses: bufbuild/buf-push-action@v1 with: input: "proto" diff --git a/.github/workflows/proto.yml b/.github/workflows/proto.yml index e9f040fc44..66d12cf4d7 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.34.0 + - uses: bufbuild/buf-setup-action@v1.38.0 - uses: bufbuild/buf-breaking-action@v1 with: input: "proto" diff --git a/.github/workflows/simulation.yml b/.github/workflows/simulation.yml new file mode 100644 index 0000000000..065769bbac --- /dev/null +++ b/.github/workflows/simulation.yml @@ -0,0 +1,69 @@ +name: Simulation +on: + workflow_call: + pull_request: + merge_group: + push: + branches: + - main + - release/v* + - feat/* + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }}-tests + cancel-in-progress: true + +jobs: + simulation-full: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: "1.22" + check-latest: true + cache: true + cache-dependency-path: go.sum + - uses: technote-space/get-diff-action@v6.1.2 + id: git_diff + with: + PATTERNS: | + **/*.go + go.mod + go.sum + **/go.mod + **/go.sum + **/Makefile + Makefile + - name: full simulation test + if: env.GIT_DIFF + run: | + make sim-full + simulation-no-inactive: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: "1.22" + check-latest: true + cache: true + cache-dependency-path: go.sum + - uses: technote-space/get-diff-action@v6.1.2 + id: git_diff + with: + PATTERNS: | + **/*.go + go.mod + go.sum + **/go.mod + **/go.sum + **/Makefile + Makefile + - name: simulation test without inactive validators + if: env.GIT_DIFF + run: | + make sim-full-no-inactive-vals \ No newline at end of file diff --git a/.gitignore b/.gitignore index 96e3251977..d93ff09e5a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,9 @@ vendor/ build/ .vscode .idea +__debug_* # docusaurus versioned docs created during build +docs/versions.json docs/versioned_docs docs/versioned_sidebars diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cc3e8a931..7f9c8e1027 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,71 @@ # CHANGELOG +## v5.1.1 + +*July 26, 2024* + +### API BREAKING + +- [Provider](x/ccv/provider) + - Fix incorrect message definitions in the proto files of the provider module + ([\#2095](https://github.com/cosmos/interchain-security/pull/2095)) + +### STATE BREAKING + +- [Provider](x/ccv/provider) + - Fix incorrect message definitions in the proto files of the provider module + ([\#2095](https://github.com/cosmos/interchain-security/pull/2095)) + +## v5.1.0 + +*July 19, 2024* + +### API BREAKING + +- General + - Remove soft opt-out feature. ([\#1995](https://github.com/cosmos/interchain-security/pull/1995)) + Backporting of ([\#1964](https://github.com/cosmos/interchain-security/pull/1964)). +- [Provider](x/ccv/provider) + - Change the UX in key assignment by returning an error if a validator tries to + reuse the same consumer key. + ([\#1998](https://github.com/cosmos/interchain-security/pull/1998)) + +### DEPENDENCIES + +- Bump [CometBFT](https://github.com/cometbft/cometbft) to + [v0.38.9](https://github.com/cometbft/cometbft/releases/tag/v0.38.9). + ([\#2013](https://github.com/cosmos/interchain-security/pull/2013)) +- Bump [ibc-go](https://github.com/cosmos/ibc-go) to + [v8.3.2](https://github.com/cosmos/ibc-go/releases/tag/v8.3.2). + ([\#2053](https://github.com/cosmos/interchain-security/pull/2053)) +- Bump [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) to +[v0.50.8](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.8) +([\#2053](https://github.com/cosmos/interchain-security/pull/2053)) + +### FEATURES + +- Remove soft opt-out feature. ([\#1995](https://github.com/cosmos/interchain-security/pull/1995)) + Backporting of ([\#1964](https://github.com/cosmos/interchain-security/pull/1964)). + +### STATE BREAKING + +- General + - Remove soft opt-out feature. ([\#1995](https://github.com/cosmos/interchain-security/pull/1995)) + Backporting of ([\#1964](https://github.com/cosmos/interchain-security/pull/1964)). + - Bump [CometBFT](https://github.com/cometbft/cometbft) to + [v0.38.9](https://github.com/cometbft/cometbft/releases/tag/v0.38.9). + ([\#2013](https://github.com/cosmos/interchain-security/pull/2013)) + - Bump [ibc-go](https://github.com/cosmos/ibc-go) to + [v8.3.2](https://github.com/cosmos/ibc-go/releases/tag/v8.3.2). + ([\#2053](https://github.com/cosmos/interchain-security/pull/2053)) + - Bump [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) to + [v0.50.8](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.8) + ([\#2053](https://github.com/cosmos/interchain-security/pull/2053)) +- [Provider](x/ccv/provider) + - Change the UX in key assignment by returning an error if a validator tries to + reuse the same consumer key. + ([\#1998](https://github.com/cosmos/interchain-security/pull/1998)) + ## v5.0.0 *May 9, 2024* diff --git a/Makefile b/Makefile index d4df5ff678..0185ed2bfd 100644 --- a/Makefile +++ b/Makefile @@ -145,6 +145,19 @@ verify-models: ../run_invariants.sh +############################################################################### +### Simulation tests ### + +# Run a full simulation test +sim-full: + cd app/provider;\ + go test -mod=readonly . -run=^TestFullAppSimulation$ -Enabled=true -NumBlocks=500 -BlockSize=200 -Commit=true -timeout 24h -v + +# Run full simulation without any inactive validators +sim-full-no-inactive-vals: + cd app/provider;\ + go test -mod=readonly . -run=^TestFullAppSimulation$ -Enabled=true -NumBlocks=500 -BlockSize=200 -Commit=true -timeout 24h -Params=no_inactive_vals_params.json -v + ############################################################################### ### Linting ### diff --git a/RELEASE_PROCESS.md b/RELEASE_PROCESS.md index 1c26c8c5ff..9d07ac1eaa 100644 --- a/RELEASE_PROCESS.md +++ b/RELEASE_PROCESS.md @@ -109,18 +109,9 @@ Once the **final release** is cut, the new changelog section must be added to ma ## 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: +Go to `main` branch and update the `docs/supported_versions.json` to include all the versions you want to publish on the docs page: ```json [ "v4.3.0", @@ -130,51 +121,6 @@ Go to `main` branch and update the `docs/versions.json` to include all the versi 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) diff --git a/TESTING.md b/TESTING.md index 1d15a56f3a..929e920775 100644 --- a/TESTING.md +++ b/TESTING.md @@ -34,6 +34,11 @@ Then, run `make docs-test` to update the test documentation. [E2E tests](tests/e2e/) run true consumer and provider chain binaries within a docker container and are relevant to the highest level of functionality. E2E tests use queries/transactions invoked from CLI to drive and validate the code. +## Simulation Tests + +[Simulation tests](app/provider/sim_test.go) are used to test the full application via the SDK's simulation framework. This framework generates random inputs and checks that certain user-defined invariants hold. +Notably, as-of-now simulation tests do not include any multi-chain testing, so can only test the provider chain on its own, without consumer chains. + ## Compatibility Tests To test compatibility between different provider and consumer versions the [E2E tests](tests/e2e/) were extended by compatibility tests. The test cases perform basic sanity tests against the selected provider and consumer versions. A selected combination of provider and consumer versions are tested on a nightly bases and can be run locally with the @@ -69,6 +74,12 @@ make test-no-cache # run E2E compatibility tests for selected provider and consumer versions make test-e2e-compatibility-tests-latest + +# run full simulation tests +make sim-full + +#run simulation tests where providerModule.max_provider_consensus_validators=stakingModule.max_validators=100 +make sim-full-no-inactive-vals ``` Alternatively you can run tests using `go test`: @@ -95,6 +106,8 @@ go run ./tests/e2e/... --local-sdk-path "/Users/bob/Documents/cosmos-sdk/" go test -fuzz= # run verbose E2E tests go run ./tests/e2e/... --local-sdk-path "/Users/bob/Documents/cosmos-sdk/" --verbose +# run simulation tests; should be run from the 0app/provider directory +go test -mod=readonly . -run=^TestFullAppSimulation$ -Enabled=true -NumBlocks=500 -BlockSize=200 -Commit=true -timeout 24h -v ``` ### Testing with Gaia as the provider diff --git a/UPGRADING.md b/UPGRADING.md index 1f25aa3d08..a3f2d5e84d 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,5 +1,100 @@ # Upgrading Replicated Security +## Unreleased + +### Provider + +Upgrading a provider from v5.1.x requires state migrations. The following migrators should be added to the upgrade handler of the provider chain: + +```go +// InitializeMaxValidatorsForExistingConsumers initializes the max validators +// parameter for existing consumers to the MaxProviderConsensusValidators parameter. +// This is necessary to avoid those consumer chains having an excessive amount of validators. +func InitializeMaxValidatorsForExistingConsumers(ctx sdk.Context, providerKeeper providerkeeper.Keeper) { + maxVals := providerKeeper.GetParams(ctx).MaxProviderConsensusValidators + for _, chainID := range providerKeeper.GetAllRegisteredConsumerChainIDs(ctx) { + providerKeeper.SetValidatorSetCap(ctx, chainID, uint32(maxVals)) + } +} + +// InitializeMaxProviderConsensusParam initializes the MaxProviderConsensusValidators parameter. +func InitializeMaxProviderConsensusParam(ctx sdk.Context, providerKeeper providerkeeper.Keeper) { + params := providerKeeper.GetParams(ctx) + if params.MaxProviderConsensusValidators == 0 { + params.MaxProviderConsensusValidators = 180 + providerKeeper.SetParams(ctx, params) + } +} +``` + +### Governance Proposals + +Legacy proposals are not supported anymore by the current version of the provider. Legacy proposals which are still active (voting period or deposit period) and contain one of the following messages that need to be migrated as described below: +- `ConsumerAdditionProposal` needs to be converted to `MsgConsumerAddition` +- `ConsumerModificationProposal` needs to be converted to `MsgConsumerModification` +- `ConsumerRemovalProposal` needs to be converted to `MsgConsumerRemoval` +- `ChangeRewardDenomsProposal` needs to be converted to `MsgChangeRewardDenoms` + +The following shows an example on how to migrate a proposal containing a legacy consumer addition proposal message. +Migration for the other messages aobve follows the same pattern. The resulting migration code has to be added to the upgrade handler of the provider chain. + +#### Migrate Legacy Proposal Content + +```go + +// MigrateLegacyConsumerAddition converts a ConsumerAdditionProposal to a MsgConsumerAdditionProposal +// and returns it as `Any` suitable to replace the legacy message. +// `authority` contains the signer address +func MigrateLegacyConsumerAddition(msg providertypes.ConsumerAdditionProposal, authority string) (*codec.Any, error) { + sdkMsg := providertypes.MsgConsumerAddition{ + ChainId: msg.ChainId, + InitialHeight: msg.InitialHeight, + GenesisHash: msg.GenesisHash, + BinaryHash: msg.BinaryHash, + SpawnTime: msg.SpawnTime, + UnbondingPeriod: msg.UnbondingPeriod, + CcvTimeoutPeriod: msg.CcvTimeoutPeriod, + TransferTimeoutPeriod: msg.TransferTimeoutPeriod, + ConsumerRedistributionFraction: msg.ConsumerRedistributionFraction, + BlocksPerDistributionTransmission: msg.BlocksPerDistributionTransmission, + HistoricalEntries: msg.HistoricalEntries, + DistributionTransmissionChannel: msg.DistributionTransmissionChannel, + Top_N: msg.Top_N, + ValidatorsPowerCap: msg.ValidatorsPowerCap, + ValidatorSetCap: msg.ValidatorSetCap, + Allowlist: msg.Allowlist, + Denylist: msg.Denylist, + Authority: authority, + MinStake: msg.MinStake, + AllowInactiveVals: msg.AllowInactiveVals, + } + return codec.NewAnyWithValue(&sdkMsg) +} + +func MigrateProposal(proposal proposal govtypes.Proposal) err { + for idx, msg := range proposal.GetMessages() { + sdkLegacyMsg, isLegacyProposal := msg.GetCachedValue().(*govtypes.MsgExecLegacyContent) + if !isLegacyProposal { + continue + } + content, err := govtypes.LegacyContentFromMessage(sdkLegacyMsg) + if err != nil { + continue + } + + msgAdd, ok := content.(*providertypes.ConsumerAdditionProposal) + if ok { + anyMsg, err := migrateLegacyConsumerAddition(*msgAdd, govKeeper.GetAuthority()) + if err != nil { + return err + } + proposal.Messages[idx] = anyMsg + } + } + return govKeeper.SetProposal(ctx, proposal) +} +``` + ## [v5.1.x](https://github.com/cosmos/interchain-security/releases/tag/v5.1.0) ### Provider @@ -14,7 +109,7 @@ Upgrade code will be executed automatically during the upgrade procedure. ### Consumer -Upgrading the consumer from `v5.0.0` to `v5.1.0` will not require state migration. +Upgrading the consumer from `v5.0.0` to `v5.1.0` will not require state migration. This guide provides instructions for upgrading to specific versions of Replicated Security. @@ -28,7 +123,7 @@ v5.0.0 was a **consumer only release**. ### Consumer -Upgrading the consumer from `v4.x` to `v5.0.0` will require state migrations. +Upgrading the consumer from `v4.x` to `v5.0.0` will require state migrations. Consumer versions `v4.0.x`, `v4.1.x`, `v4.2.x`, `v4.3.x` and `v4.4.x` can cleanly be upgraded to `v5.0.0`. @@ -42,7 +137,7 @@ Upgrade code will be executed automatically during the upgrade procedure. ### Consumer -Upgrading the consumer from `v4.0.0` to `v4.4.0` will not require state migration. +Upgrading the consumer from `v4.0.0` to `v4.4.0` will not require state migration. This guide provides instructions for upgrading to specific versions of Replicated Security. @@ -50,7 +145,7 @@ This guide provides instructions for upgrading to specific versions of Replicate ### Provider -Upgrading a provider from `v4.2.0` to `v4.3.0` requires state migrations that will be done automatically via the upgrade module. +Upgrading a provider from `v4.2.0` to `v4.3.0` requires state migrations that will be done automatically via the upgrade module. ### Consumer @@ -105,17 +200,17 @@ func InitICSEpochs(ctx sdk.Context, pk providerkeeper.Keeper, sk stakingkeeper.K ## [v4.0.x](https://github.com/cosmos/interchain-security/tree/release/v4.0.x) -`v4.0.x` sets the minimum required version of Go to `1.21`, see https://github.com/cosmos/interchain-security/blob/release/v4.0.x/go.mod#L3. +`v4.0.x` sets the minimum required version of Go to `1.21`, see https://github.com/cosmos/interchain-security/blob/release/v4.0.x/go.mod#L3. -### Provider +### Provider -Upgrading a provider from `v3.3.0` to `v4.0.0` will require state migrations, see https://github.com/cosmos/interchain-security/blob/release/v4.0.x/x/ccv/provider/migrations/migrator.go#L31. +Upgrading a provider from `v3.3.0` to `v4.0.0` will require state migrations, see https://github.com/cosmos/interchain-security/blob/release/v4.0.x/x/ccv/provider/migrations/migrator.go#L31. -### Consumer +### Consumer -***Note that consumer chains can upgrade directly from `v3.1.0` to `v4.0.0`.*** +***Note that consumer chains can upgrade directly from `v3.1.0` to `v4.0.0`.*** -Upgrading a consumer from `v3.2.0` to `v4.0.0` will not require state migration, however, upgrading directly from `v3.1.0` to `v4.0.0` will require state migrations, see https://github.com/cosmos/interchain-security/blob/release/v4.0.x/x/ccv/consumer/keeper/migrations.go#L22. +Upgrading a consumer from `v3.2.0` to `v4.0.0` will not require state migration, however, upgrading directly from `v3.1.0` to `v4.0.0` will require state migrations, see https://github.com/cosmos/interchain-security/blob/release/v4.0.x/x/ccv/consumer/keeper/migrations.go#L22. In addition, the following migration needs to be added to the upgrade handler of the consumer chain: ```golang @@ -139,15 +234,15 @@ func migrateICSOutstandingDowntime(ctx sdk.Context, keepers *upgrades.UpgradeKee ## [v3.3.x](https://github.com/cosmos/interchain-security/tree/release/v3.2.x) -### Provider +### Provider -Upgrading the provider from `v2.x.y` to `v3.3.0` will not require state migration. +Upgrading the provider from `v2.x.y` to `v3.3.0` will not require state migration. ## [v3.2.x](https://github.com/cosmos/interchain-security/tree/release/v3.2.x) `v3.2.0` bumps IBC to `v7.3`. As a result, `legacy_ibc_testing` is not longer required and was removed, see https://github.com/cosmos/interchain-security/pull/1185. This means that when upgrading to `v3.2.0`, any customized tests relying on `legacy_ibc_testing` need to be updated. -### Consumer +### Consumer Upgrading the consumer from either `v3.0.0` or `v3.1.0` to `v3.2.0` will require state migrations, see https://github.com/cosmos/interchain-security/blob/release/v3.2.x/x/ccv/consumer/keeper/migration.go#L25. @@ -157,13 +252,13 @@ Upgrading the consumer from either `v3.0.0` or `v3.1.0` to `v3.2.0` will require The following should be considered as complementary to [Cosmos SDK v0.47 UPGRADING.md](https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc2/UPGRADING.md). -#### Protobuf +#### Protobuf Protobuf code generation, linting and formatting have been updated to leverage the `ghcr.io/cosmos/proto-builder:0.11.5` docker container. Replicated Security protobuf definitions are now packaged and published to [buf.build/cosmos/interchain-security](https://buf.build/cosmos/interchain-security) via CI workflows. The `third_party/proto` directory has been removed in favour of dependency management using [buf.build](https://docs.buf.build/introduction). #### App modules -Legacy APIs of the `AppModule` interface have been removed from ccv modules. For example, for +Legacy APIs of the `AppModule` interface have been removed from ccv modules. For example, for ```diff - // Route implements the AppModule interface @@ -213,10 +308,10 @@ import ( ## [v2.0.x](https://github.com/cosmos/interchain-security/releases/tag/v2.0.0) -### Provider +### Provider Upgrading a provider from `v1.1.0-multiden` to `v2.0.0` will require state migrations. See [migration.go](https://github.com/cosmos/interchain-security/blob/v2.0.0/x/ccv/provider/keeper/migration.go). ### Consumer -Upgrading a consumer from `v1.2.0-multiden` to `v2.0.0` will NOT require state migrations. +Upgrading a consumer from `v1.2.0-multiden` to `v2.0.0` will NOT require state migrations. diff --git a/app/consumer/app.go b/app/consumer/app.go index 8eb64bf5c6..28e0e642e5 100644 --- a/app/consumer/app.go +++ b/app/consumer/app.go @@ -490,7 +490,6 @@ func New( vestingtypes.ModuleName, ibcconsumertypes.ModuleName, ) - app.SetPreBlocker(app.PreBlocker) app.MM.SetOrderEndBlockers( crisistypes.ModuleName, diff --git a/app/provider/app.go b/app/provider/app.go index 23262161b4..0e6e4b3b80 100644 --- a/app/provider/app.go +++ b/app/provider/app.go @@ -8,7 +8,11 @@ import ( "os" "path/filepath" + dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/gogoproto/proto" + "github.com/cosmos/ibc-go/modules/capability" + capabilitykeeper "github.com/cosmos/ibc-go/modules/capability/keeper" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" "github.com/cosmos/ibc-go/v8/modules/apps/transfer" ibctransferkeeper "github.com/cosmos/ibc-go/v8/modules/apps/transfer/keeper" ibctransfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" @@ -26,7 +30,7 @@ import ( reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" "cosmossdk.io/client/v2/autocli" "cosmossdk.io/core/appmodule" - + "cosmossdk.io/log" storetypes "cosmossdk.io/store/types" "cosmossdk.io/x/evidence" evidencekeeper "cosmossdk.io/x/evidence/keeper" @@ -35,6 +39,7 @@ import ( "cosmossdk.io/x/upgrade" upgradekeeper "cosmossdk.io/x/upgrade/keeper" upgradetypes "cosmossdk.io/x/upgrade/types" + "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" @@ -50,16 +55,20 @@ import ( "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" "github.com/cosmos/cosmos-sdk/std" + "github.com/cosmos/cosmos-sdk/testutil/testdata/testpb" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/types/msgservice" + sigtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/ante" authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" "github.com/cosmos/cosmos-sdk/x/auth/posthandler" + authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + txmodule "github.com/cosmos/cosmos-sdk/x/auth/tx/config" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/auth/vesting" vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" @@ -93,29 +102,20 @@ import ( "github.com/cosmos/cosmos-sdk/x/slashing" slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" - "github.com/cosmos/cosmos-sdk/x/staking" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/ibc-go/modules/capability" - capabilitykeeper "github.com/cosmos/ibc-go/modules/capability/keeper" - capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" - "cosmossdk.io/log" abci "github.com/cometbft/cometbft/abci/types" tmjson "github.com/cometbft/cometbft/libs/json" tmos "github.com/cometbft/cometbft/libs/os" - dbm "github.com/cosmos/cosmos-db" appencoding "github.com/cosmos/interchain-security/v5/app/encoding" testutil "github.com/cosmos/interchain-security/v5/testutil/integration" + no_valupdates_genutil "github.com/cosmos/interchain-security/v5/x/ccv/no_valupdates_genutil" + no_valupdates_staking "github.com/cosmos/interchain-security/v5/x/ccv/no_valupdates_staking" 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" - - "github.com/cosmos/cosmos-sdk/testutil/testdata/testpb" - sigtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" - txmodule "github.com/cosmos/cosmos-sdk/x/auth/tx/config" ) const ( @@ -143,16 +143,12 @@ var ( gov.NewAppModuleBasic( []govclient.ProposalHandler{ paramsclient.ProposalHandler, - ibcproviderclient.ConsumerAdditionProposalHandler, - ibcproviderclient.ConsumerRemovalProposalHandler, - ibcproviderclient.ConsumerModificationProposalHandler, - ibcproviderclient.ChangeRewardDenomsProposalHandler, }, ), mint.AppModuleBasic{}, slashing.AppModuleBasic{}, distr.AppModuleBasic{}, - staking.AppModuleBasic{}, + no_valupdates_staking.AppModuleBasic{}, upgrade.AppModuleBasic{}, evidence.AppModuleBasic{}, @@ -349,12 +345,7 @@ func New( authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) - // Remove the ConsumerRewardsPool from the group of blocked recipient addresses in bank - // this is required for the provider chain to be able to receive tokens from - // the consumer chain - bankBlockedAddrs := app.ModuleAccountAddrs() - delete(bankBlockedAddrs, authtypes.NewModuleAddress( - providertypes.ConsumerRewardsPool).String()) + bankBlockedAddrs := ComputeBankBlockedAddrs(app) app.BankKeeper = bankkeeper.NewBaseKeeper( appCodec, @@ -374,15 +365,6 @@ func New( authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()), authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()), ) - app.MintKeeper = mintkeeper.NewKeeper( - appCodec, - runtime.NewKVStoreService(keys[minttypes.StoreKey]), - app.StakingKeeper, - app.AccountKeeper, - app.BankKeeper, - authtypes.FeeCollectorName, - authtypes.NewModuleAddress(govtypes.ModuleName).String(), - ) app.DistrKeeper = distrkeeper.NewKeeper( appCodec, runtime.NewKVStoreService(keys[distrtypes.StoreKey]), @@ -457,19 +439,6 @@ func New( runtime.ProvideCometInfoService(), ) - govConfig := govtypes.DefaultConfig() - app.GovKeeper = govkeeper.NewKeeper( - appCodec, - runtime.NewKVStoreService(keys[govtypes.StoreKey]), - app.AccountKeeper, - app.BankKeeper, - app.StakingKeeper, - app.DistrKeeper, - app.MsgServiceRouter(), - govConfig, - authtypes.NewModuleAddress(govtypes.ModuleName).String(), - ) - app.ProviderKeeper = ibcproviderkeeper.NewKeeper( appCodec, keys[providertypes.StoreKey], @@ -484,13 +453,43 @@ func New( app.AccountKeeper, app.DistrKeeper, app.BankKeeper, - *app.GovKeeper, + govkeeper.Keeper{}, // will be set after the GovKeeper is created authtypes.NewModuleAddress(govtypes.ModuleName).String(), authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()), authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()), authtypes.FeeCollectorName, ) + govConfig := govtypes.DefaultConfig() + app.GovKeeper = govkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[govtypes.StoreKey]), + app.AccountKeeper, + app.BankKeeper, + // use the ProviderKeeper as StakingKeeper for gov + // because governance should be based on the consensus-active validators + app.ProviderKeeper, + app.DistrKeeper, + app.MsgServiceRouter(), + govConfig, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + // set the GovKeeper in the ProviderKeeper + app.ProviderKeeper.SetGovKeeper(*app.GovKeeper) + + app.MintKeeper = mintkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[minttypes.StoreKey]), + // use the ProviderKeeper as StakingKeeper for mint + // because minting should be based on the consensus-active validators + app.ProviderKeeper, + app.AccountKeeper, + app.BankKeeper, + authtypes.FeeCollectorName, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + // gov router must be set after the provider keeper is created // otherwise the provider keeper will not be able to handle proposals (will be nil) govRouter := govv1beta1.NewRouter() @@ -505,7 +504,11 @@ func New( govtypes.NewMultiGovHooks(app.ProviderKeeper.Hooks()), ) - providerModule := ibcprovider.NewAppModule(&app.ProviderKeeper, app.GetSubspace(providertypes.ModuleName)) + providerModule := ibcprovider.NewAppModule( + &app.ProviderKeeper, + app.GetSubspace(providertypes.ModuleName), + keys[providertypes.StoreKey], + ) app.TransferKeeper = ibctransferkeeper.NewKeeper( appCodec, @@ -536,7 +539,7 @@ func New( // NOTE: Any module instantiated in the module manager that is later modified // must be passed by reference here. app.MM = module.NewManager( - genutil.NewAppModule( + no_valupdates_genutil.NewAppModule( app.AccountKeeper, app.StakingKeeper, app, @@ -552,7 +555,7 @@ func New( mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, nil, app.GetSubspace(minttypes.ModuleName)), slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(slashingtypes.ModuleName), app.interfaceRegistry), distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(distrtypes.ModuleName)), - staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), + no_valupdates_staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), upgrade.NewAppModule(&app.UpgradeKeeper, app.AccountKeeper.AddressCodec()), evidence.NewAppModule(app.EvidenceKeeper), @@ -571,10 +574,6 @@ func New( govtypes.ModuleName: gov.NewAppModuleBasic( []govclient.ProposalHandler{ paramsclient.ProposalHandler, - ibcproviderclient.ConsumerAdditionProposalHandler, - ibcproviderclient.ConsumerRemovalProposalHandler, - ibcproviderclient.ConsumerModificationProposalHandler, - ibcproviderclient.ChangeRewardDenomsProposalHandler, }, ), }) @@ -624,6 +623,8 @@ func New( providertypes.ModuleName, ) + // NOTE: provider module needs to come after the staking module, since + // it needs the information the staking module provides to compute validator updates. app.MM.SetOrderEndBlockers( crisistypes.ModuleName, govtypes.ModuleName, @@ -650,6 +651,8 @@ func New( // NOTE: Capability module must occur first so that it can initialize any capabilities // so that other modules that want to create or claim capabilities afterwards in InitChain // can do so safely. + // NOTE: The provider module must come after genutils and staking, since it relies on the + // information about the validators these modules provide to compute validator updates. app.MM.SetOrderInitGenesis( capabilitytypes.ModuleName, authtypes.ModuleName, @@ -659,7 +662,6 @@ func New( slashingtypes.ModuleName, govtypes.ModuleName, minttypes.ModuleName, - crisistypes.ModuleName, ibcexported.ModuleName, evidencetypes.ModuleName, ibctransfertypes.ModuleName, @@ -669,6 +671,7 @@ func New( vestingtypes.ModuleName, providertypes.ModuleName, consensusparamtypes.ModuleName, + crisistypes.ModuleName, // crisis needs to be last so that the genesis state is consistent when it checks invariants ) app.MM.RegisterInvariants(&app.CrisisKeeper) @@ -678,6 +681,15 @@ func New( panic(err) } + // create the simulation manager and define the order of the modules for deterministic simulations + overrideModules := map[string]module.AppModuleSimulation{ + authtypes.ModuleName: auth.NewAppModule(app.appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), + } + app.sm = module.NewSimulationManagerFromAppModules(app.MM.Modules, overrideModules) + + // register the store decoders for simulation tests + app.sm.RegisterStoreDecoders() + // Note this upgrade handler is just an example and may not be exactly what you need to implement. // See https://docs.cosmos.network/v0.45/building-modules/upgrade.html app.UpgradeKeeper.SetUpgradeHandler( @@ -778,6 +790,17 @@ func New( return app } +// Computes the addresses that should be blocked by the Bank module. +// We remove the ConsumerRewardsPool from the group of blocked recipient addresses in bank. +// This is required for the provider chain to be able to receive tokens from +// the consumer chain +func ComputeBankBlockedAddrs(app *App) map[string]bool { + bankBlockedAddrs := app.ModuleAccountAddrs() + delete(bankBlockedAddrs, authtypes.NewModuleAddress( + providertypes.ConsumerRewardsPool).String()) + return bankBlockedAddrs +} + // Name returns the name of the App func (app *App) Name() string { return app.BaseApp.Name() } diff --git a/app/provider/no_inactive_vals_params.json b/app/provider/no_inactive_vals_params.json new file mode 100644 index 0000000000..127beaca79 --- /dev/null +++ b/app/provider/no_inactive_vals_params.json @@ -0,0 +1,4 @@ +{ + "max_validators": 100, + "max_provider_consensus_validators": 100 +} \ No newline at end of file diff --git a/app/provider/sim_test.go b/app/provider/sim_test.go new file mode 100644 index 0000000000..ef938b0b53 --- /dev/null +++ b/app/provider/sim_test.go @@ -0,0 +1,100 @@ +package app_test + +import ( + "fmt" + "math/rand" + "os" + "testing" + + "cosmossdk.io/store" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/server" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + "github.com/cosmos/cosmos-sdk/x/simulation" + simcli "github.com/cosmos/cosmos-sdk/x/simulation/client/cli" + + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + + providerapp "github.com/cosmos/interchain-security/v5/app/provider" + + spew "github.com/davecgh/go-spew/spew" +) + +func init() { + simcli.GetSimulatorFlags() +} + +// interBlockCacheOpt returns a BaseApp option function that sets the persistent +// inter-block write-through cache. +func interBlockCacheOpt() func(*baseapp.BaseApp) { + return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager()) +} + +// fauxMerkleModeOpt returns a BaseApp option to use a dbStoreAdapter instead of +// an IAVLStore for faster simulation speed. +func fauxMerkleModeOpt(bapp *baseapp.BaseApp) { + bapp.SetFauxMerkleMode() +} + +func TestFullAppSimulation(t *testing.T) { + config := simcli.NewConfigFromFlags() + config.ChainID = "provi" + + // if no seed is provided (aka we use the default seed), override this by choosing an actually random seed + if config.Seed == simcli.DefaultSeedValue { + fmt.Printf("Default seed value %v detected, using random seed value\n", simcli.DefaultSeedValue) + config.Seed = int64(rand.Uint32()) + } + + fmt.Println("========================================") + fmt.Println("Running with the configuration:") + fmt.Println(spew.Sdump(config)) + fmt.Println("========================================") + + db, dir, logger, skip, err := simtestutil.SetupSimulation(config, "leveldb-app-sim", "Simulation", simcli.FlagVerboseValue, simcli.FlagEnabledValue) + if skip { + t.Skip("skipping application simulation") + } + require.NoError(t, err, "simulation setup failed") + + defer func() { + require.NoError(t, db.Close()) + require.NoError(t, os.RemoveAll(dir)) + }() + + appOptions := make(simtestutil.AppOptionsMap, 0) + appOptions[flags.FlagHome] = providerapp.DefaultNodeHome + appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue + + app := providerapp.New(logger, db, nil, true, appOptions, fauxMerkleModeOpt, interBlockCacheOpt(), baseapp.SetChainID("provi")) + require.Equal(t, "interchain-security-p", app.Name()) + + encoding := providerapp.MakeTestEncodingConfig() + + genesisState := providerapp.NewDefaultGenesisState(encoding.Codec) + + // run randomized simulation + _, simParams, simErr := simulation.SimulateFromSeed( + t, + os.Stdout, + app.BaseApp, + simtestutil.AppStateFn(encoding.Codec, app.SimulationManager(), genesisState), + simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 + simtestutil.SimulationOperations(app, app.AppCodec(), config), + providerapp.ComputeBankBlockedAddrs(app), + config, + app.AppCodec(), + ) + + // export state and simParams before the simulation error is checked + err = simtestutil.CheckExportSimulation(app, config, simParams) + require.NoError(t, err) + require.NoError(t, simErr) + + if config.Commit { + simtestutil.PrintStats(db) + } +} diff --git a/app/sovereign/app.go b/app/sovereign/app.go index 328fb5e4b9..b5653995b6 100644 --- a/app/sovereign/app.go +++ b/app/sovereign/app.go @@ -116,7 +116,7 @@ import ( const ( AppName = "interchain-security-s" upgradeName = "v07-Theta" // arbitrary name, define your own appropriately named upgrade - AccountAddressPrefix = "cosmos" + AccountAddressPrefix = "consumer" ) var ( @@ -602,6 +602,7 @@ func New( app.SetAnteHandler(anteHandler) app.SetInitChainer(app.InitChainer) + app.SetPreBlocker(app.PreBlocker) app.SetBeginBlocker(app.BeginBlocker) app.SetEndBlocker(app.EndBlocker) diff --git a/app/sovereign/export.go b/app/sovereign/export.go index 53e9f6fee1..6c7750d4c2 100644 --- a/app/sovereign/export.go +++ b/app/sovereign/export.go @@ -81,7 +81,6 @@ func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []str // withdraw all validator commission app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { valBz, err := app.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) - if err != nil { panic(err) } diff --git a/cmd/interchain-security-sd/main.go b/cmd/interchain-security-sd/main.go index 99f6b9b371..dbd46acc60 100644 --- a/cmd/interchain-security-sd/main.go +++ b/cmd/interchain-security-sd/main.go @@ -6,11 +6,14 @@ import ( 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" ) func main() { + appparams.SetAddressPrefixes(app.AccountAddressPrefix) rootCmd := cmd.NewRootCmd() if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil { diff --git a/docs/README.md b/docs/README.md index 4c4146ca4b..2598b1d9ff 100644 --- a/docs/README.md +++ b/docs/README.md @@ -24,6 +24,7 @@ $ npm run build This command generates static content into the `build` directory and can be served using any static contents hosting service. +This is not intended for local development but it is used during the deploy sequence. # Adding versions @@ -53,9 +54,12 @@ The script above will create `versioned_docs` and `versioned_sidebars` directori To view the docs run: ```shell +cp supported_versions.json versions.json # needed to show the version dropdown npm run start ``` +Remove `versions.json` after use to prevent interference with local documentation. + 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 ) @@ -71,3 +75,23 @@ The script switches branches while building the versioned docs - **please note t 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 `./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. + + +# Scripts and make commands + +`build_deploy.sh` script builds the documentation output directory for serving static HTML files. It should be executed on the remote server. + +`build_local.sh` will build the documentation locally by running `npm run build`. You can check the built web page by running `npm run serve` after the build command completes. + +`sync_versions.sh` will fetch and build all docs versions specified in `supported_versions.json`. It is intended to be executed on the remote server. + +`versions.json` must remain empty or be removed from your worktree. Avoid pushing it to GitHub. +* this file specifies which versions will be displayed in the supported versions drop down on the docs page +* instead of using it, the supported versions should live in `supported_versions.json` +* this file should only be populated during deployment and never on your local machine + +# Building on remote + +Building on remote host is handled by [deploy-docs.yml workflow](.github/workflows/deploy-docs.yml). + +It executes `./sync_versions.sh` and `./build_deploy.sh` scripts and allows the output of the build process to be served by github pages. diff --git a/docs/build_deploy.sh b/docs/build_deploy.sh index ba262e43b2..a5cfb5676f 100755 --- a/docs/build_deploy.sh +++ b/docs/build_deploy.sh @@ -2,6 +2,7 @@ # build versioned docs prepared by sync_versions.sh echo "building docusaurus main docs" +cp supported_versions.json versions.json npm ci && npm run build # copy "legacy" docs directory into the final build directory diff --git a/docs/docs/adrs/_category_.json b/docs/docs/adrs/_category_.json index 71f8f3e6ce..bfac4b5113 100644 --- a/docs/docs/adrs/_category_.json +++ b/docs/docs/adrs/_category_.json @@ -1,4 +1,4 @@ { "label": "ADRs", - "position": 6 + "position": 7 } diff --git a/docs/docs/adrs/adr-017-allowing-inactive-validators.md b/docs/docs/adrs/adr-017-allowing-inactive-validators.md index 17925bf68f..df0e611e8b 100644 --- a/docs/docs/adrs/adr-017-allowing-inactive-validators.md +++ b/docs/docs/adrs/adr-017-allowing-inactive-validators.md @@ -10,7 +10,7 @@ title: ICS with Inactive Provider Validators ## Status -Proposed +Accepted ## Context @@ -58,19 +58,109 @@ achieved without further changes to staking or reward distributions, because sim 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. +* Introduce the `MaxProviderConsensusValidators` parameter to the provider module, which is the number of validators that the provider module will send to the consensus engine. * 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. +* Introduce extra per-consumer-chain parameters: + * `MinStake`: is the minimum amount of stake a validator must have to be considered for validation on the consumer chain. + * `AllowInactiveVals`: is a boolean that determines whether validators that are not part of the active set on the provider chain can validate on the consumer chain. If this is set to `true`, validators outside the active set on the provider chain can validate on the consumer chain. If this is set to `true`, validators outside the active set on the provider chain cannot validate on the consumer chain. ## 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 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. +## Testing Scenarios + +In the following, +- bonded validators refers to all validators that have bonded stake, +- active validators refers to the validators that take part in consensus, +- inactive validators refers to bonded validators that are not active validators. + +### Scenario 1: Inactive validators should not be considered by governance + +Inactive validators should not be considered for the purpose of governance. +In particular, the quorum should depend only on active validators. + +We test this by: +* creating a provider chain (either with 3 active validators, or with only 1 active validator), a quorum of 50%, and 3 validators with alice=300, bob=299, charlie=299 stake +* we create a governance proposal +* alice votes for the proposal +* we check that the proposal has the right status: + * in the scenario where we have 3 active validators, the proposal *should not* have passed, because alice alone is not enough to fulfill the quorum + * in the scenario where we have 1 active validator, the proposal *should* have passed, because alice is the only active validator, and thus fulfills the quorum + +Tested by the e2e tests `inactive-provider-validators-governance` (scenario with 1 active val) and `inactive-provider-validators-governance-basecase` (scenario with 3 active vals). + +### Scenario 2: Inactive validators should not get rewards from the provider chain + +Inactive validators should not get rewards from the provider chain. + +This can be tested by starting a provider chain with inactive validators and checking the rewards of inactive validators. + +Checked as part of the e2e test `inactive-provider-validators-on-consumer`. + +### Scenario 3: Inactive validators should get rewards from consumer chains + +An inactive validator that is validating on a consumer chain should receive rewards in the consumer chain token. + +Checked as part of the e2e test `inactive-provider-validators-on-consumer`. + +### Scenario 4: Inactive validators should not get slashed/jailed for downtime on the provider chain + +This can be tested by having an inactive validator go offline on the provider chain for long enough to accrue downtime. +The validator should be neither slashed nor jailed for downtime. + +Checked as part of the e2e test `inactive-provider-validators-on-consumer`. + +### Scenario 5: Inactive validators *should* get jailed for consumer downtime on the provider chain + +This can be tested by having an inactive validator go offline on a consumer chain for long enough to accrue downtime. +The consumer chain should send a SlashPacket to the provider chain, which should jail the validator. + +Checked as part of the e2e test `inactive-provider-validators-on-consumer`. + +### Scenario 6: Inactive validators should not be counted when computing the minimum power in the top N + +This can be tested like this: +* Start a provider chain with validator powers alice=300, bob=200, charlie=100 and 2 max provider consensus validators + * So alice and bob will validate on the provider +* Start a consumer chain with top N = 51%. + * Without inactive validators, this means both alice and bob have to validate. But since charlie is inactive, this means bob is *not* in the top N +* Verify that alice is in the top N, but bob is not + +Checked as part of the e2e test `inactive-vals-topN`. + +### Scenario 7: Mint does not consider inactive validators + +To compute the inflation rate, only the active validators should be considered. + +We can check this by querying the inflation rate change over subsequent blocks. + +We start a provider chain with these arguments +* 3 validators with powers alice=290, bob=280, charlie=270 +* either 1 or 3 active validators +* a bonded goal of 300 tokens (this is given in percent, but we simplify here) + +If we have 3 validators active, then the inflation rate should *decrease* between blocks, because the bonded goal is exceeded as all validators are bonded. +If we have only 1 validator active, then the inflation rate should *increase* between blocks, because the bonded goal is not met. + +Checked as part of the e2e tests `inactive-vals-mint` (scenario with 1 active val) and `mint-basecase` (scenario with 3 active vals). + +### Scenarios 8: Inactive validators can validate on consumer chains + +An inactive validator can opt in and validate on consumer chains (if min stake allows it) + +Checked as part of the e2e test `inactive-provider-validators-on-consumer`. + +### Scenario 9: MinStake parameters is respected + +Validators that don't meet the criteria for a consumer chain cannot validate on it. + +Checked in the e2e tests `min-stake`. + ## Consequences ### Positive diff --git a/docs/docs/adrs/adr-018-remove-vscmatured.md b/docs/docs/adrs/adr-018-remove-vscmatured.md new file mode 100644 index 0000000000..f7cdd55492 --- /dev/null +++ b/docs/docs/adrs/adr-018-remove-vscmatured.md @@ -0,0 +1,188 @@ +--- +sidebar_position: 19 +title: Remove VSCMatured Packets +--- +# ADR 018: Remove VSCMatured Packets + +## Changelog +* 19/06/2024: Create initial draft + +## Status + +Accepted + +## Context + +> The idea of removing VSCMatured packets was first suggested by [Jae Kwon](https://github.com/jaekwon) over a conversation in 2022. +> As a result of that conversation, [an ADR was proposed](https://github.com/cosmos/interchain-security/pull/712) to halt consumer chain if VSC packets are no longer received. +> The ADR was not accepted due to other considerations regarding the safety of consumer chains. +> See [this blog post](https://informal.systems/blog/learning-to-live-with-unbonding-pausing) for more details. + +The consumer module on the consumer chains is a representation of the Hub’s staking module, i.e., it provides an _asynchronous_ view of the voting powers and indirectly of the locked collateral. +The key word here is _asynchronous_, which means that (in theory) there is no bound on the lag between the Hub’s view of stake and the consumer’s view of stake. +The reasons for this asynchrony are relaying delays and chain liveness (e.g., a consumer could be down for a long period of time without affecting the liveness of the staking module on the Hub). + +The current version of ICS uses `VSCMaturedPackets` to create on the consumers a _partially synchronous_ view of the Hub’s staking module. +Partially synchronous means that the lag between the Hub’s view of stake and the consumer’s view of stake is bounded, because consumers that exceed this lag are forcibly removed from the protocol. +Basically, unlocking collateral from the Hub is being delayed until the consumers’ `UnbondingPeriod` elapses. +The reason the view is only partially synchronous is that eventually the collateral is unlocked, i.e., if `VSCMaturedPackets` are not received from a consumer for `VscTimeoutPeriod` (default: 5 weeks), then the consumer is removed from ICS and the collateral is unlocked. +Note that keeping the stake locked “forever” would affect the Hub’s liveness, so it’s not a viable option. + +The issue is that whatever attack is possible with an asynchronous view of the staking module, it is eventually possible with the partially synchronous view as well. +For example, an attacker could wait for `VscTimeoutPeriod` for the collateral to be unlocked and then send invalid headers to third-party chains that are not aware the consumer's collateral is no longer locked on the Hub (i.e., the consumer is no longer part of ICS). + +Moreover, with the introduction of [PSS](./adr-015-partial-set-security.md), a consumer’s validator set could “lie” about its `UnbondingPeriod` elapsing by sending `VSCMaturedPackets` earlier. +This would result in a discrepancy between a light client’s view of the `UnbondingPeriod` and the actual Hub’s `UnbondingPeriod`. + +## Decision + +This ADR proposes the removal of `VSCMaturedPackets`. The reason is twofold. +First, `VSCMaturedPackets` provide a "false" sense of correctness as the attack described above is still possible. +Second, `VSCMaturedPackets` add considerable complexity to the ICS protocol -- an extra message plus the pausing of unbonding operations that can affect the UX. + +To simplify the upgrading process, removing `VSCMaturedPackets` can be done in two releases: + +* (R1) Update the provider to drop `VSCMaturedPackets`. +* (R2) Update the consumer to stop sending `VSCMaturedPackets`. + +As a result, once the provider chain runs R1, the consumers can start upgrading to R2. + +### Provider Changes (R1) + +#### Parameters + +Deprecate the `InitTimeoutPeriod` and `VscTimeoutPeriod` parameters. + +#### State + +Add the following key prefix to the state: + +- `ConsumerAddrsToPruneV2BytePrefix` -- the byte prefix for storing consumer validators addresses that need to be pruned. + These are stored as + ``` + ConsumerAddrsToPruneV2BytePrefix | len(chainID) | chainID | ts -> (consumer_address1, consumer_address2, ...) + ``` + where `ts` is the timestamp at which the consumer validators addresses can be pruned. + +Migrate the consumer validator addresses stored under the `ConsumerAddrsToPruneBytePrefix` to the new prefix `ConsumerAddrsToPruneV2BytePrefix`. +Note that these consumer validators addresses are stored as +``` +ConsumerAddrsToPruneBytePrefix | len(chainID) | chainID | vscID -> (consumer_address1, consumer_address2, ...) +``` +where `vscID` is the ID of the first VSCPacket sent after these consumer validator addresses were changed. +These means that we can use the `VscSendTimestamp`s to compute the timestamps when these consumer validator addresses can be pruned, i.e., +```golang +func MigrateConsumerAddrsToPrune() + iterator := sdk.KVStorePrefixIterator(store, []byte{providertypes.ConsumerAddrsToPruneBytePrefix}) + for ; iterator.Valid(); iterator.Next() { + chainID, vscID, _ := providertypes.ParseChainIdAndUintIdKey(providertypes.ConsumerAddrsToPruneBytePrefix, iterator.Key()) + // use the VscSendTimestamp index to compute the timestamp at which this consumer addresses can be pruned + vscSendTimestampKey := providertypes.ChainIdAndUintIdKey(providertypes.VscSendTimestampBytePrefix, chainID, vscID) + timeBz := store.Get(vscSendTimestampKey) + sentTime, _ := sdk.ParseTimeBytes(timeBz) + pruneTs := sentTime.Add(sk.UnbondingTime(ctx)) + + var addrs providertypes.AddressList + addrs.Unmarshal(iterator.Value()) + + for _, addr := range addrs.Addresses { + consumerAddr := providertypes.NewConsumerConsAddress(addr) + pk.AppendConsumerAddrsToPrune(ctx, chainID, pruneTs, consumerAddr) + } + } +} +``` + +Remove the following key prefixes from the state. Note that these removals require state migration. + +- `MaturedUnbondingOpsByteKey` -- the byte key that stores the list of all unbonding operations ids that have matured from a consumer chain perspective. +- `UnbondingOpBytePrefix` -- the byte prefix that stores a record of all the ids of consumer chains that need to unbond before a given unbonding operation can unbond on this chain. +- `UnbondingOpIndexBytePrefix` -- the byte prefix of the index for looking up which unbonding operations are waiting for a given consumer chain to unbond. +- `InitTimeoutTimestampBytePrefix` -- the byte prefix for storing the init timeout timestamp for a given consumer chainID. +- `VscSendTimestampBytePrefix` -- the byte prefix for storing the list of VSC sending timestamps for a given consumer chainID. +- `ConsumerAddrsToPruneBytePrefix` -- the byte prefix for storing the mapping from VSC IDs to consumer validators addresses that need to be pruned. + +#### State Transitions + +Removing `VSCMaturedPackets` affects three ICS sub-protocols (see [HandleVSCMaturedPacket](https://github.com/cosmos/interchain-security/blob/v4.2.0/x/ccv/provider/keeper/relay.go#L51)): unbonding operations pausing, `VSCPackets` timeout, and key assignment pruning. +The first two are no longer needed, while the third (key assignment pruning) needs to be redesigned to not depend on `VSCMaturedPackets`. + +**Removing unbonding operations pausing:** + +- Make the `AfterUnbondingInitiated` hook a no-op. As a result, unbonding operations are no longer paused. +- Stop calling the `UnbondingCanComplete` method from the staking keeper. This entails, it is no longer necessary to append `MaturedUnbondingOps` and the `completeMaturedUnbondingOps` method can be removed. +- Note, that during the upgrade, all unbonding operations stored under the `UnbondingOpBytePrefix` prefix need to be completed (via the `UnbondingCanComplete` method from the staking keeper). +- Remove the init timeout timestamp logic from the following methods: `CreateConsumerClient`, `SetConsumerChain`, and `EndBlockCCR`. + +**Removing `VSCPackets` timeout:** + +- Stop setting VSC send timestamps when sending `VSCPackets`. +- Stop removing the VSC send timestamps when receiving `VSCMaturedPackets`. +- Remove the logic from `EndBlockCCR` that checks if the first VSC send timestamp in iterator plus `VscTimeoutPeriod` exceeds the current block time. + +**Redesign key assignment pruning.** The reason for keeping "old" consumer addresses is to enable slashing / jailing validators that misbehave on consumer chains, +i.e., the slashing logic uses the `GetProviderAddrFromConsumerAddr` method that accesses the mapping from validator addresses on consumer chains to validator addresses on the provider chain (`ValidatorsByConsumerAddrBytePrefix`). +Thus, "old" consumer addresses are no longer needed after the provider's `UnbondingPeriod` elapses. +This means that once a validator changes its key on a consumer, we can prune the address corresponding to the "old" key after `UnbondingPeriod`. +This requires the following changes: + +- Adapt the `AppendConsumerAddrsToPrune()` method to use the timestamp at which it is safe to prune the consumer validator address (instead of the current `vscID`). +- Add a new method `ConsumeConsumerAddrsToPrune(ts)` that returns the list of consumer addresses that can be pruned at timestamp `ts`. +- Adapt the `PruneKeyAssignments()` method to call `ConsumeConsumerAddrsToPrune(ctx.BlockTime())`. +- Call the `PruneKeyAssignments()` method from every `EndBlock()` instead of calling it from `HandleVSCMaturedPacket()`. + +#### Queries + +Remove the `oldest_unconfirmed_vsc` query. + +### Consumer Changes (R2) + +#### Parameters + +Given that currently relayers use the consumer `UnbondingPeriod` (see `ConsumerParams`), this param cannot be deprecated. +Note that Hermes queries the `UnbondingPeriod` for sanity checks and to set the default trusting period when it is not specified. +As a result, the `UnbondingTime` method from the staking interface will continue to be used to retrieve the consumer's `UnbondingPeriod`. + +#### State + +Remove the following key prefixes from the state: + +- `PacketMaturityTimeBytePrefix` -- the byte prefix that will store maturity time for each received VSC packet + +Note that these removals require state migration. + +#### State Transitions + +To stop the consumer chains from sending `VSCMaturedPackets`, it is sufficient to not store the maturity time of `VSCPacket`s when receiving them, i.e., do not call `SetPacketMaturityTime` from the `OnRecvVSCPacket()` method. +Note that eventually, no additional `VSCMaturedPackets` will be added to the sending queue as `QueueVSCMaturedPackets` iterates over elapsed maturity times. +In addition, to clean up the code, the `QueueVSCMaturedPackets` must be removed. + +#### Messages + +`VSCMaturedPacketData` is deprecated. +Note that this is a wire-breaking change -- older consumer versions will send `VSCMaturedPackets` and older provider versions will expect to receive `VSCMaturedPackets`. + +## Consequences + +### Positive + +- Remove feature that provides a "false" sense of correctness. +- Remove unnecessary complexity, from both ICS and Cosmos SDK. +- Remove one IBC packet and, thus, reduce relaying cost. +- Remove unbonding pausing logic that could affect the UX. + +### Negative + +- Large refactor that might introduce unexpected bugs. +- Consumer chains are no longer removed if the duration between creating a client and creating the CCV channel exceeds `InitTimeoutPeriod`. + This means that if the CCV channel is not created on time, the client expires and the consumer chain can no longer start without a ClientUpdate proposal or re-submitting a `ConsumerAdditionProposal`. + +### Neutral + +- Consumer chains are no longer removed after a `VscTimeoutPeriod` of inactivity. + Note that consumers are still removed if their CCV channel expires, which usually happens after two weeks instead of five weeks (the default value for `VscTimeoutPeriod`). + +## References + +- [PR #712](https://github.com/cosmos/interchain-security/pull/712) -- Proposal to set an expiration date on the consumer chain updated on each VSCPacket received to ensure the chain is up-to-date. +- [Learning to Live with “Unbonding Pausing”](https://informal.systems/blog/learning-to-live-with-unbonding-pausing) blog post. diff --git a/docs/docs/adrs/adr-019-permissionless-ics.md b/docs/docs/adrs/adr-019-permissionless-ics.md new file mode 100644 index 0000000000..59b13659a4 --- /dev/null +++ b/docs/docs/adrs/adr-019-permissionless-ics.md @@ -0,0 +1,348 @@ +--- +sidebar_position: 20 +title: Permissionless ICS +--- +# ADR 019: Permissionless Interchain Security + +## Changelog +* 27th of June, 2024: Initial draft + +## Status + +Accepted + +## Context +Currently, a consumer chain can join _Interchain Security_ (ICS) only through a [governance proposal](../features/proposals.md). +A governance proposal was needed before the introduction of [Partial Set Security](../features/partial-set-security.md) (PSS) +because validators were required to validate a consumer chain. However, after the introduction of PSS, a consumer chain can +be either _Top N_ or _Opt In_. If a chain is an Opt In chain, then no validator is required to validate this chain unless they choose to. +Because of this, we can launch an Opt In consumer chain without going through a governance proposal. + +This ADR presents _Permissionless_ ICS, a way in which an [_Opt In_](adr-015-partial-set-security.md) consumer chain can join +ICS without needing a governance proposal but by simply issuing a transaction. + +## Decision +In Permissionless ICS, launching an Opt In chain is **only** possible through a transaction and not through a [`MsgConsumerAddition`](https://github.com/cosmos/interchain-security/blob/v5.1.0/proto/interchain_security/ccv/provider/v1/tx.proto#L111) +proposal. Naturally, Permissionless ICS does not eliminate governance proposals, as proposals are still necessary for Top N chains. +Because of this, this ADR outlines a solution that also refactors governance proposals (i.e., `MsgConsumerAddition`, `MsgConsumerModification`, and `MsgConsumerRemoval`) +so that Top N and Opt In chains can share as much functionality as possible. +Note, that to make the distinction between governance-proposed versus transaction-launched chains clearer, in Permissionless ICS, +we can only launch, update, or stop Top N chains with governance proposals, and we can only launch, update, or stop Opt In chains with transactions. +Additionally, a Top N chain can transform to an Opt In chain through a gov proposal, but for simplicity, in this first +iteration of Permissionless, an Opt In chain cannot transform to a Top N chain. + +### The Phases of a Consumer Chain + +We first present the notion of an _owner_ of a consumer chain before showing the specific phases of a consumer chain. + +**Owner.** A consumer chain has an _owner_, which is simply an address. Only the owner can interact (i.e., launch, update, or stop) +with the chain. The owner of an Opt In chain is the one who signed the initial transaction to register a consumer chain (more on this later). +Naturally, an Opt In chain can change its owner at any point. The owner of a Top N chain is the account of the governance module. +Therefore, any changes on a Top N chain have to go through governance proposals. + +A consumer chain can reside in four phases: i) _registered_, ii) _initialized_, iii) _launched_, and iv) _stopped_ phase as seen +in the diagram below: +![Phases of a consumer chain](./figures/adr19_phases_of_a_consumer_chain.png) + +**Registered phase.** In the _registered phase_, a consumer chain is assigned a unique identifier that identifies a consumer chain +that can later be used to interact with the specific consumer chain (e.g., when a validator opts in on a chain). +After a chain has been registered, it can later be initialized and then launched. Specifically, Permissionless ICS introduces +a `MsgRegisterConsumer` message that can be used to register **both** Top N and Opt In consumer chains. +In the registered phase, it is not yet known if the consumer chain would end up being a Top N or an Opt In chain and hence +the owner of the consumer chain at this phase is the one that signed the `MsgRegisterConsumer`. +Note that currently, a consumer chain is registered when first proposed through a `MsgConsumerAddition` proposal +message but with Permissionless ICS, the `MsgConsumerAddition` is deprecated and chains have to issue a `MsgRegisterConsumer` message instead. +A consumer chain in the registered phase might not launch, e.g., a later governance proposal might not pass or +the Opt In chain might never be initialized. + +**Initialized phase.** The _initialized phase_ means that the chain has set all the needed parameters to launch but has +not yet launched. To initialize an Opt In chain, the owner of the chain has to issue a `MsgInitializeConsumer` message +and potentially a `MsgUpdateConsumer` if they want to set up specific parameters (e.g., [power-shaping features](https://cosmos.github.io/interchain-security/features/power-shaping)). +Similarly, a Top N chain has to issue the same two messages (i.e, `MsgInitializeConsumer`, `MsgUpdateConsumer`) as part of a +governance proposal and if the governance proposal passes, the consumer chain is considered to be initialized. The moment +a governance proposal is proposed for a Top N chain, the owner changes to be the account of the governance module. +While in the initialized phase, an Opt In chain can choose to change the consumer chain parameters, such as `spawnTime`, etc. +by issuing a new `MsgInitializeConsumer` or `MsgUpdateConsumer` messages. +This is not the case for Top N chains, where a `MsgUpdateConsumer` can only be issued after a consumer +chain [has launched](https://github.com/cosmos/interchain-security/blob/v5.1.0/x/ccv/provider/keeper/legacy_proposal.go#L89). + +**Launched phase.** In the _launched phase_ the consumer chain is running and is consuming a subset of the validator set +of the provider. When the [`spawnTime`](https://github.com/cosmos/interchain-security/blob/v5.1.0/proto/interchain_security/ccv/provider/v1/provider.proto#L57) +passes and [at least one validator has opted in](https://github.com/cosmos/interchain-security/blob/v5.1.0/x/ccv/provider/keeper/proposal.go#L430) +the chain can launch and moves to the launched phase. Note that a Top N chain can launch if and only if the `spawnTime` has passed and +the initialization proposal has successfully passed. While in launched phase, a consumer chain can choose to modify +its parameters through `MsgUpdateConsumer`. Naturally, only the owner of the chain can issue `MsgUpdateConsumer`, thus +for Top N chains, the chain can be updated only through a governance proposal that contains a `MsgUpdateConsumer`. + +**Stopped phase.** Lastly, the owner of a chain can choose to exit ICS by executing a `MsgRemoveConsumer`. +After some period of time (e.g., provider's unbonding period), all state related to the stopped consumer chain can be removed. +We keep track of the state of the consumer chain for some period, so that we are able to punish validators for misbehaviours +that occurred before the consumer chain stopped. + +Note that everything described so far and everything that follows applies to consumer chains that transition from standalone chains as well. + +### From `chainId` to `consumerId` +A hindrance in moving to Permissionless ICS is [chain-id squatting](https://forum.cosmos.network/t/pss-permissionless-vs-premissioned-lite-opt-in-consumer-chains/12984/17). +In a permissionless setting, anyone could issue a transaction to launch a consumer chain with a `chainId` that might already be used by some other consumer chain. This is a problem +because in the current design the majority of stored state for a consumer chain is indexed using the `chainId` as the key (e.g., +see [key used to store client ids](https://github.com/cosmos/interchain-security/blob/v5.1.0/x/ccv/provider/types/keys.go#L245)). +To tackle this problem, in Permissionless ICS, we introduce the `consumerId` that defines a consumer chain and is simply +an increasing counter (i.e., `counter`), thus we can support multiple consumer chains with the same `chainId`. +Another way to understand this is with an analogy between consumer chains and IBC clients: Imagine having multiple IBC clients +that each point to different consumer chains, but all share the exact same `chainId`. It is then up to the user to select the +appropriate client (i.e., `clientId`) based on the actual chain they want to communicate with. Similarly, there can be multiple +consumer chains with the exact same `chainId`, and it is the responsibility of the validators to choose the one they wish +to interact with by providing the right `consumerId`. + +Note that with Permissionless ICS, all interactions on a consumer chain have to use the `consumerId` instead of the `chainId`. +For example, if a validator opts in on a chain using `MsgOptIn`, the validator has to provide the `consumerId`. To also +provide the `consumerId` for Top N consumers chains, we store a mapping between `proposalID` to `consumerId`. This storing +takes place in the [`AfterProposalSubmission`](https://github.com/cosmos/cosmos-sdk/blob/v0.50.8/x/gov/types/hooks.go#L19) hook. +Specifically, for the equivocation evidence, we update the `MsgSubmitConsumerMisbehaviour` and `MsgSubmitConsumerDoubleVoting` messages to include the `consumerId`, +and change [Hermes](https://github.com/informalsystems/hermes) to include `consumerId` in those constructed messages as well. +Hermes can find out the `consumerId` by querying the provider's `clientId` for some consumer chain (i.e., `query ccvconsumer provider-info`) +and then asking the provider chain for the `consumerId` that corresponds to this `clientId`. To do this, we need to store +the `clientId` to `consumerId` association on the provider and introduce a query to retrieve the `clientId` +given the `consumerId`. + +#### State +As a result of using `consumerId`, we have to migrate a substantial chunk of state to re-index it using `consumerId` as the key. +Currently, in ICS we have state that is indexed by a multitude of [keys](https://github.com/cosmos/interchain-security/blob/v5.1.0/x/ccv/provider/types/keys.go#L40). +In the table below, we see the ones that are associated with a `chainId` and how often state under those keys gets updated. +Additionally, for each key, the table shows whose action can lead to the setting or deletion of the state associated with that key. +An action can stem either from: i) a consumer chain (e.g., through a `MsgUpdateConsumer` message, an IBC packet sent over to the provider, etc.), +ii) a provider chain (e.g., at the end of a block some action is taken), or by iii) a validator (e.g., through a `MsgAssignConsumerKey` message) +or a combination of them. + +| Key | Description | Who can set this? | Who can delete this? | How often are `chainId`-associated keys updated? | +|-----------------------------------------|------------------------------------------------------------------------------------------------------------------------------|:--------------------------------:|:--------------------------------:|----------------------------------------------------------------------------------------------------------| +| `ChainToChannelBytePrefix` | Stores the CCV `channelID` for a specific chain | consumer chain | consumer chain | Only once (during set up) | +| `ChannelToChainBytePrefix` | Stores `chainId` for a specific channel | consumer chain | consumer chain | Only once (during set up) | +| `ChainToClientBytePrefix` | Stores the `clientID` for a specific chain | consumer chain | consumer chain | Only once (during set up) | +| `PendingCAPBytePrefix` | Stores pending consumer addition proposals | consumer chain | provider chain | Only once (for successful proposal) | +| `PendingCRPBytePrefix` | Stores pending consumer removal proposals | consumer chain | provider chain | Only once (for successful proposal) | +| `ConsumerGenesisBytePrefix` | Stores the consumer genesis for a specific chain | consumer chain | consumer chain | Only once (during set up) | +| `SlashAcksBytePrefix` | Stores slash acks for a specific consumer chain | consumer chain | provider chain | Every time we receive a Slash packet | +| `PendingVSCsBytePrefix` | Stores `VSCPacket`s for a specific consumer chain | provider chain | provider chain | Every [epoch](https://github.com/cosmos/interchain-security/blob/v5.1.0/docs/docs/adrs/adr-014-epochs.md) | +| `ConsumerValidatorsBytePrefix` | Stores consumer key per validator per consumer chain | validator | consumer chain | Every `MsgAssignConsumerKey` or `MsgOptIn` | +| `ValidatorsByConsumerAddrBytePrefix` | Stores consumer to provider validator address | validator | consumer or provider chain | Every `MsgAssignConsumerKey` or `MsgOptIn` | +| `EquivocationEvidenceMinHeightBytePrefix`| Stores min height for a consumer chain | consumer chain | consumer chain | Only once (during set up) | +| `ProposedConsumerChainByteKey` | Stores `proposalID`s for consumer chains with proposals in the voting period | not applicable for Opt In chains | not applicable for Opt In chains | Created when the proposal is submitted and deleted when the proposal's voting period ends | +| `ConsumerValidatorBytePrefix` | Stores consumer validators for a specific chain | validator | validator or consumer chain | Potentially at every epoch | +| `OptedInBytePrefix` | Stores opted-in validators for a specific chain | validator | validator or consumer chain | Potentially at every block | +| `TopNBytePrefix` | Stores whether a consumer chain is Top N or not | not applicable for Opt In chains | not applicable for Opt In chains | Every parameter update | +| `ValidatorsPowerCapPrefix` | Stores the power cap of a chain | consumer chain | consumer chain | Every parameter update | +| `ValidatorSetCapPrefix` | Stores the set cap of a chain | consumer chain | consumer chain | Every parameter update | +| `AllowlistPrefix` | Stores the allowlist of a chain | consumer chain | consumer chain | Every parameter update | +| `DenylistPrefix` | Stores the denylist of a chain | consumer chain | consumer chain | Every parameter update | +| `ConsumerRewardsAllocationBytePrefix` | Stores the ICS rewards per chain | consumer or provider chain | provider chain | Every IBC transfer packet that sends rewards to the provider | +| `ConsumerCommissionRatePrefix` | Commission rate per chain per validator | validator | consumer chain | Every `MsgSetConsumerCommissionRate` message | +| `MinimumPowerInTopNBytePrefix` | Stores the minimum power needed to opt in for a chain | not applicable for Opt In chains | not applicable for Opt In chains | Every epoch | +| `ConsumerAddrsToPruneV2BytePrefix` | Stores consumer addresses to be pruned (as part of `VSCMaturedPacket`s deprecation) | validator or provider chain | provider chain | Every `MsgAssignConsumerKey` or `MsgOptIn` and later during actual pruning | + +Everything stored under one of the above keys is associated with a `chainId` and has to be migrated to new state under a `consumerId`. + +### New Messages +In this section, we describe the new messages (i.e., `MsgRegisterConsumer`, `MsgInitializeConsumer`, `MsgUpdateConsumer` and `MsgRemoveConsumer`) +that Permissionless ICS introduces. +Then, we describe how to utilize these messages with our existing codebase. + +#### Register a Consumer Chain +We first have to register a chain before launching it, irrespectively of whether it is Top N or Opt In. +This is done through the following message: +```protobuf +message MsgRegisterConsumer { + // the registration record that contains information for the registered chain + ConsumerRegistrationRecord registration_record; +} +``` + +where `ConsumerRegistrationRecord` contains information about the to-be-launched consumer chain before it launches. + +```protobuf +message ConsumerRegistrationRecord { + // the title of the chain to-be-launched + string title; + // the description of the chain to-be-launched + string description; + // the chain id of the new consumer chain + string chain_id; +} +``` + +This response of this message contains a single `string`, that is the `consumerId` for this registered consumer chain and sets +a consumer chain in its registered phase. With the returned `consumerId`, validators can already opt in on the consumer +chain to show their potential interest on the chain. + +The owner of the consumer chain is the one that signed the `MsgRegisterConsumer` message. + +To prevent an attacker spamming the system by creating bogus consumer chains, we set a fixed cost for sending a `MsgRegisterConsumer` (configurable via a parameter). + +#### Initialize a Consumer Chain +To move an Opt In consumer chain to its initialized phase, we issue a `MsgInitializeConsumer` message that is as follows: + +```protobuf +message MsgInitializeConsumer { + // consumer id of the to-be-updated consumer chain + string consumer_id; + // the initialization record that contains initialization parameters for the upcoming chain + ConsumerInitializationRecord initialization_record; +} +``` +where `ConsumerInitializationRecord` contains the following: +```protobuf +message ConsumerInitializationRecord { + // ---------- ---------- ---------- + // Following fields are used when the consumer chain launches and are not needed by the provider afterwards. + // ---------- ---------- ---------- + + // the proposed initial height of new consumer chain. + // For a completely new chain, this will be {0,1}. However, it may be + // different if this is a chain that is converting to a consumer chain. + ibc.core.client.v1.Height initial_height; + // The hash of the consumer chain genesis state without the consumer CCV + // module genesis params. It is used for off-chain confirmation of + // genesis.json validity by validators and other parties. + bytes genesis_hash; + // The 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. + bytes binary_hash; + // spawn time is the time on the provider chain at which the consumer chain + // genesis is finalized and all validators will be responsible for starting + // their consumer chain validator node. + google.protobuf.Timestamp spawn_time; + // Unbonding period for the consumer, + // which should be smaller than that of the provider in general. + google.protobuf.Duration unbonding_period; + + + // ---------- ---------- ---------- + // Following fields are used to construct the consumer genesis of the to-be-launched consumer chain + // and are set up as params on the consumer chain. Those params can then be directly modified by the consumer chain. + // ---------- ---------- ---------- + + // Sent CCV related IBC packets will timeout after this duration + google.protobuf.Duration ccv_timeout_period; + // Sent transfer related IBC packets will timeout after this duration + google.protobuf.Duration transfer_timeout_period; + // The fraction of tokens allocated to the consumer redistribution address + // during distribution events. The fraction is a string representing a + // decimal number. For example "0.75" would represent 75%. + string consumer_redistribution_fraction; + // BlocksPerDistributionTransmission is the number of blocks between + // ibc-token-transfers from the consumer chain to the provider chain. On + // sending transmission event, `consumer_redistribution_fraction` of the + // accumulated tokens are sent to the consumer redistribution address. + int64 blocks_per_distribution_transmission; + // The number of historical info entries to persist in store. + // This param is a part of the cosmos sdk staking module. In the case of + // a ccv enabled consumer chain, the ccv module acts as the staking module. + int64 historical_entries; + // The ID of a token transfer channel used for the Reward Distribution + // sub-protocol. If DistributionTransmissionChannel == "", a new transfer + // channel is created on top of the same connection as the CCV channel. + // Note that transfer_channel_id is the ID of the channel end on the consumer + // 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; +} +``` + +`ConsumerInitializationRecord` contains _almost_ everything that is contained in [`MsgConsumerAddition`](https://github.com/cosmos/interchain-security/blob/v5.1.0/proto/interchain_security/ccv/provider/v1/tx.proto#L111). +Note that as part of this work, we deprecate [`ConsumerAdditionProposal`](https://github.com/cosmos/interchain-security/blob/v5.1.0/proto/interchain_security/ccv/provider/v1/provider.proto#L30). + +For each `consumerId`, we store its corresponding `ConsumerInitializationRecord`. For Top N chains, we can perform this +store by using the [`AfterProposalVotingPeriodEnded`](https://github.com/cosmos/cosmos-sdk/blob/v0.50.8/x/gov/types/hooks.go#L52). + +Note that we need to extensively check the fields of the provided `ConsumerInitializationRecord` to guarantee that no consumer +chain launches with problematic parameters (e.g., we need to have maximum length for the `chainId`, etc.). +As a starter we look into the [usual validity conditions](https://github.com/cosmos/interchain-security/blob/v5.1.0/x/ccv/provider/types/msg.go#L244). + +For all chains in the initialized phase, we keep a mapping between `consumerId` and the underlying `ConsumerInitializationRecord`. +This way, we can respond to queries that ask for all the consumer chain's parameters. For example, retrieving the +`spawn_time` of consumer chain with a given `consumerId`. + +`MsgInitializeConsumer` can be executed multiple times for the same Opt In consumer chain during its initialized phase +to potentially change its to-be-launched parameters (e.g., `spawnTime`). + +A Top N can move to the initialized phase only if the owner of the registered chain issues a governance proposal +with two messages, `MsgInitializeConsumer` and `MsgUpdateConsumer`, and the proposal passes. + +#### Modify a Consumer Chain +We reuse the [MsgConsumerModification](https://github.com/cosmos/interchain-security/blob/v5.1.0/proto/interchain_security/ccv/provider/v1/tx.proto#L294) +message to update parameters of an Opt In chain that is in its initialized or launched phase, but rename the message +to `MsgUpdateConsumer`. This message can only be executed by the owner of a consumer +chain and hence only the owner can change the parameters (e.g., `validators_power_cap`, `allowlist`, etc.) +of the consumer chain. Recall that if the consumer chain is a Top N chain, then the owner is the address of the +governance account. + +We refactor `MsgUpdateConsumer` to be as follows: +```protobuf +message MsgUpdateConsumer { + // consumer id of the to-be-updated consumer chain + string consumer_id; + ConsumerUpdateRecord update_record; +} +``` + +where `ConsumerUpdateRecord` contains the following: +``` +message ConsumerUpdateRecord { + // `owner_address` cannot be modified by a Top N chain, unless it moves to an Opt In chain (i.e., `top_N == 0`) + string owner_address; + uint32 top_N; + uint32 validators_power_cap; + uint32 validator_set_cap; + repeated string allowlist; + repeated string denylist; +} +``` + +Note, that even though a consumer chain is initialized with all the arguments in `ConsumerUpdateRecord`, +the `MsgUpdateConsumer` updates only the `owner_address` and the `consumer_update_record`. This is because +all the other arguments are either useless (e.g., `spawnTime`) after a chain has started, or can be updated directly +by the consumer chain params (e.g., `consumer_redistribution_fraction`). + +#### Remove (Stop) a Consumer Chain +We reuse the `MsgConsumerRemoval` (renamed to `MsgRemoveConsumer`) so we can stop any Opt In chain at any point in time. +Note that all relevant state for this consumer chain remains on the provider's state before getting removed after the time +of an unbonding period (of the provider) has passed. This is to enable potential slashing for any infraction that might have been incurred until now. +Note however that we never recycle previously-used `consumerId`s. Naturally, this message can only be issued by the owner of the consumer chain. + +```protobuf +message MsgRemoveConsumer { + // the consumerId as returned by `MsgRegisterConsumer` + string consumer_id; + // the time on the provider chain at which all validators are responsible to + // stop their consumer chain validator node + google.protobuf.Timestamp stop_time; +} +``` + +#### Examples of Launching a Consumer Chain +The figures below depict some examples of some of the phases a consumer chain resides in to launch. + +![Examples of a launching consumer chain](./figures/adr19_flows_of_launching_a_consumer_chain.png) + + +### Additional Modifications +We need to perform multiple migrations. All state needs to be reindex based on a `consumerId` instead of the `chainId`. +Because we only have two consumer chains at the moment, this is not going to be an expensive migration even if we have some live +consumer chains that are being voted upon. Similarly, all the messages, queries, etc. would need to be changed to operate on a `consumerId` +instead of a `chainId`. + +It is **important** to migrate any ongoing `ConsumerAdditionProposal`s when we upgrade before we actually deprecate `ConsumerAdditionProposal`s. + +## Consequences + +### Positive +- Easier to launch an Opt In consumer chain because no governance is required. + +### Negative +- Extensive migration and overhaul of existing code base (as part of API-breaking changes) that could lead to bugs and more work in auditing this. + + +## References +[CHIPs Discussion phase: Permissionless ICS](https://forum.cosmos.network/t/chips-discussion-phase-permissionless-ics/13955) +[Chain-id squatting](https://forum.cosmos.network/t/pss-permissionless-vs-premissioned-lite-opt-in-consumer-chains/12984/17) \ No newline at end of file diff --git a/docs/docs/adrs/figures/adr19_flows_of_launching_a_consumer_chain.excalidraw b/docs/docs/adrs/figures/adr19_flows_of_launching_a_consumer_chain.excalidraw new file mode 100644 index 0000000000..4e9c57cce0 --- /dev/null +++ b/docs/docs/adrs/figures/adr19_flows_of_launching_a_consumer_chain.excalidraw @@ -0,0 +1,3277 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "ellipse", + "version": 432, + "versionNonce": 656013628, + "index": "a1", + "isDeleted": false, + "id": "SI0HtgaAMGQsMr1Y7bXrA", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 512.4765625, + "y": 248.29980468749991, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 143.13281250000014, + "height": 137.65234375000014, + "seed": 1039402003, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "A97toxLGgigcGTMSLzFRE" + }, + { + "id": "zeENeKW4p1ce_DWzf-29f", + "type": "arrow" + }, + { + "id": "qIMMSItT6zGcYO5ZUPcar", + "type": "arrow" + } + ], + "updated": 1721898441262, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 772, + "versionNonce": 276787644, + "index": "a1V", + "isDeleted": false, + "id": "A97toxLGgigcGTMSLzFRE", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 547.7299002905518, + "y": 306.9585237065766, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 72.41595458984375, + "height": 20, + "seed": 262978077, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898441262, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "initialized", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "SI0HtgaAMGQsMr1Y7bXrA", + "originalText": "initialized", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "ellipse", + "version": 225, + "versionNonce": 1063465660, + "index": "a2", + "isDeleted": false, + "id": "y5DOCeanki7NLMac-YZU_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 963.30078125, + "y": 252.48730468749994, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 113.27734375, + "height": 113.27734375, + "seed": 303821331, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "id": "zeENeKW4p1ce_DWzf-29f", + "type": "arrow" + }, + { + "type": "text", + "id": "_lR4nCPdco_VXP-hHHxFx" + }, + { + "id": "hoUATc742qsSXGtb3zXhk", + "type": "arrow" + }, + { + "id": "6m4pEWBrlMrOpXdR2qMdd", + "type": "arrow" + } + ], + "updated": 1721898441262, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 69, + "versionNonce": 241531708, + "index": "a2G", + "isDeleted": false, + "id": "_lR4nCPdco_VXP-hHHxFx", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 987.2738821091236, + "y": 299.07638760228764, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 65.23196411132812, + "height": 20, + "seed": 2116710579, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898441262, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "launched", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "y5DOCeanki7NLMac-YZU_", + "originalText": "launched", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 818, + "versionNonce": 352917936, + "index": "a4", + "isDeleted": false, + "id": "-Ezkh_kc6H-iZ8_N1SUwz", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 728.4853515625, + "y": 18.888671874999943, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 462.22369116544724, + "height": 40, + "seed": 176740147, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "JQNfOufG1Sc3Ln1iHc6v8", + "type": "arrow" + } + ], + "updated": 1721977797787, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "proposal with MsgInitializeConsumer & MsgUpdateConsumer\nto start a Top N chain", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "proposal with MsgInitializeConsumer & MsgUpdateConsumer\nto start a Top N chain", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 3380, + "versionNonce": 1334694320, + "index": "a5", + "isDeleted": false, + "id": "JQNfOufG1Sc3Ln1iHc6v8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 863.2801567822762, + "y": 66.26969410176584, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 208.7131111905685, + "height": 55.219229040456455, + "seed": 428750077, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721977807991, + "link": null, + "locked": false, + "startBinding": { + "elementId": "-Ezkh_kc6H-iZ8_N1SUwz", + "focus": 0.14349370214591656, + "gap": 7.381022226765893 + }, + "endBinding": { + "elementId": "_VXIQppCSNb6MIFQLmDdy", + "focus": 0.1824872496377905, + "gap": 2.2713741813428783 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -106.6058242763097, + 51.06445336557442 + ], + [ + -208.7131111905685, + 55.219229040456455 + ] + ] + }, + { + "type": "text", + "version": 1068, + "versionNonce": 1922126672, + "index": "a6", + "isDeleted": false, + "id": "9NwhWEx9olM4fARFt5hZe", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 189.74827575683594, + "y": -17.066406250000057, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 308.01581835746765, + "height": 80, + "seed": 304812115, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "NAUT1CeeaW6ntAtOKbXGS", + "type": "arrow" + } + ], + "updated": 1721977650326, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "tx to register\nan Opt In or Top N consumer chain\n(MsgRegisterConsumer)\nowner = signer of MsgRegisterConsumer", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "tx to register\nan Opt In or Top N consumer chain\n(MsgRegisterConsumer)\nowner = signer of MsgRegisterConsumer", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 331, + "versionNonce": 1834637060, + "index": "a7", + "isDeleted": false, + "id": "zeENeKW4p1ce_DWzf-29f", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 663.2551165518071, + "y": 319.75738423211857, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 299.31331302963645, + "height": 5.083241028410356, + "seed": 783684445, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898441294, + "link": null, + "locked": false, + "startBinding": { + "elementId": "9VJjnsFNvss8g8B_wchEr", + "focus": -1.1953607235764194, + "gap": 6.181092330381375 + }, + "endBinding": { + "elementId": "y5DOCeanki7NLMac-YZU_", + "focus": -0.08074296871041202, + "gap": 1.0000000000000995 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 299.31331302963645, + -5.083241028410356 + ] + ] + }, + { + "type": "text", + "version": 418, + "versionNonce": 1250834364, + "index": "a8", + "isDeleted": false, + "id": "9VJjnsFNvss8g8B_wchEr", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 669.0216369628906, + "y": 325.93847656249994, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 252.0958709716797, + "height": 60, + "seed": 1053753011, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "zeENeKW4p1ce_DWzf-29f", + "type": "arrow" + } + ], + "updated": 1721898441262, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "chain's spawnTime passed\nAND\nat least one validator opted in", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "chain's spawnTime passed\nAND\nat least one validator opted in", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 417, + "versionNonce": 7026096, + "index": "aF", + "isDeleted": false, + "id": "FgsFB8OrGtx1-nlYRY0Uz", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1100.889877319336, + "y": 155.14941406249994, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 229.75984984636307, + "height": 40, + "seed": 51301117, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "hoUATc742qsSXGtb3zXhk", + "type": "arrow" + } + ], + "updated": 1721979486389, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "MsgUpdateConsumer for both\nTop N and Opt In chains", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgUpdateConsumer for both\nTop N and Opt In chains", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 936, + "versionNonce": 1806394884, + "index": "aG", + "isDeleted": false, + "id": "hoUATc742qsSXGtb3zXhk", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1043.1909748738744, + "y": 254.20084659464027, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 81.51926732005495, + "height": 77.65472732322138, + "seed": 684481907, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898441295, + "link": null, + "locked": false, + "startBinding": { + "elementId": "y5DOCeanki7NLMac-YZU_", + "focus": -0.6276577497929622, + "gap": 3.005298194275724 + }, + "endBinding": { + "elementId": "y5DOCeanki7NLMac-YZU_", + "focus": 0.6500438788511836, + "gap": 6.714013400352123 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 81.51926732005495, + -47.95974594543503 + ], + [ + 34.8604592337133, + 29.69498137778635 + ] + ] + }, + { + "type": "ellipse", + "version": 356, + "versionNonce": 411466172, + "index": "aK", + "isDeleted": false, + "id": "U2EhanfFFSg1GNh4uQ9Xo", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 973.2587890625, + "y": 528.0859375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 113.27734375, + "height": 113.27734375, + "seed": 420095965, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "RfEXevpkB5rowEFa4mHDD" + }, + { + "id": "6m4pEWBrlMrOpXdR2qMdd", + "type": "arrow" + } + ], + "updated": 1721898441262, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 209, + "versionNonce": 294117948, + "index": "aL", + "isDeleted": false, + "id": "RfEXevpkB5rowEFa4mHDD", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 999.7359029831471, + "y": 574.6750204147877, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 60.22393798828125, + "height": 20, + "seed": 1217602621, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898441262, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "stopped", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "U2EhanfFFSg1GNh4uQ9Xo", + "originalText": "stopped", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 518, + "versionNonce": 1178907908, + "index": "aM", + "isDeleted": false, + "id": "6m4pEWBrlMrOpXdR2qMdd", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1027.530607098985, + "y": 367.313746063125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1.0811756614666592, + "height": 152.58658700381386, + "seed": 354868061, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898441295, + "link": null, + "locked": false, + "startBinding": { + "elementId": "y5DOCeanki7NLMac-YZU_", + "focus": -0.1267451224869414, + "gap": 2.042177966448442 + }, + "endBinding": { + "elementId": "U2EhanfFFSg1GNh4uQ9Xo", + "focus": -0.014589591451189068, + "gap": 8.198352788681127 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 1.0811756614666592, + 152.58658700381386 + ] + ] + }, + { + "type": "ellipse", + "version": 1043, + "versionNonce": 1743463856, + "index": "aP", + "isDeleted": false, + "id": "_VXIQppCSNb6MIFQLmDdy", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 527.1933593750001, + "y": 49.049845487915945, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 125.71093750000004, + "height": 126.94419050538774, + "seed": 1424716579, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "ND9wpVssAkaXAtA1CWR-j" + }, + { + "id": "qIMMSItT6zGcYO5ZUPcar", + "type": "arrow" + }, + { + "id": "JQNfOufG1Sc3Ln1iHc6v8", + "type": "arrow" + }, + { + "id": "NAUT1CeeaW6ntAtOKbXGS", + "type": "arrow" + }, + { + "id": "SP-m3QTzlcLoU-_bmfZeN", + "type": "arrow" + } + ], + "updated": 1721977737205, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1438, + "versionNonce": 303490108, + "index": "aQ", + "isDeleted": false, + "id": "ND9wpVssAkaXAtA1CWR-j", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 550.919332346884, + "y": 102.6403917713115, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 78.36793518066406, + "height": 20, + "seed": 704059075, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898441262, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "registered", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "_VXIQppCSNb6MIFQLmDdy", + "originalText": "registered", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 2498, + "versionNonce": 1811695536, + "index": "aR", + "isDeleted": false, + "id": "qIMMSItT6zGcYO5ZUPcar", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 598.616944950635, + "y": 175.68190954353736, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 95.2174182195338, + "height": 71.14063431860131, + "seed": 711212803, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721977823250, + "link": null, + "locked": false, + "startBinding": { + "elementId": "_VXIQppCSNb6MIFQLmDdy", + "focus": 0.7831196417981204, + "gap": 1 + }, + "endBinding": { + "elementId": "SI0HtgaAMGQsMr1Y7bXrA", + "focus": -0.9808503194655256, + "gap": 2.5866858268130386 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 93.66378808277591, + 56.75434794509022 + ], + [ + -1.5536301367578846, + 71.14063431860131 + ] + ] + }, + { + "type": "text", + "version": 894, + "versionNonce": 1163437392, + "index": "aS", + "isDeleted": false, + "id": "tONaGSodwA-i1Tr_ZvpZf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 703.2325744628906, + "y": 211.6659109933036, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 63.951934814453125, + "height": 40, + "seed": 1173975117, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721977821204, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "proposal\npasses", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "proposal\npasses", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 2698, + "versionNonce": 1501320528, + "index": "aW", + "isDeleted": false, + "id": "NAUT1CeeaW6ntAtOKbXGS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 295.2339916085824, + "y": 72.54770775067982, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 219.9931787394372, + "height": 44.86816430307441, + "seed": 713565901, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721977650641, + "link": null, + "locked": false, + "startBinding": { + "elementId": "9NwhWEx9olM4fARFt5hZe", + "focus": 0.5220455825778929, + "gap": 9.614114000679876 + }, + "endBinding": { + "elementId": "_VXIQppCSNb6MIFQLmDdy", + "focus": -0.040896091175469475, + "gap": 12.037638560264035 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 49.778852818030145, + 44.86816430307441 + ], + [ + 219.9931787394372, + 43.27181702781314 + ] + ] + }, + { + "type": "arrow", + "version": 2638, + "versionNonce": 2034843580, + "index": "aX", + "isDeleted": false, + "id": "QJHjyYDxam9jq3ifrE6rE", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 553.4126684100163, + "y": 172.7898695168439, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 88.24899059693328, + "height": 80.70767694042144, + "seed": 901908284, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898441262, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -87.03045455607186, + 30.651422159720436 + ], + [ + 1.218536040861423, + 80.70767694042144 + ] + ] + }, + { + "type": "text", + "version": 954, + "versionNonce": 1840703408, + "index": "aY", + "isDeleted": false, + "id": "OSTXsQ9rLudhM8FpW1Tmi", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 237.52793884277344, + "y": 181.43050856817348, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 220.76788437366486, + "height": 60, + "seed": 1590512260, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721981417956, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "tx to initialize a registered\nOpt In chain\n(MsgInitializeConsumer)", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "tx to initialize a registered\nOpt In chain\n(MsgInitializeConsumer)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 618, + "versionNonce": 1408338864, + "index": "aZ", + "isDeleted": false, + "id": "YfLjGiz8pwcgaeYLbqk17", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1046.5860595703125, + "y": 407.42009190150674, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 198.86389088630676, + "height": 40, + "seed": 200186756, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721977871422, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "MsgRemoveConsumer for\nTop N and Opt In chains", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgRemoveConsumer for\nTop N and Opt In chains", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 1258, + "versionNonce": 1493934000, + "index": "aa", + "isDeleted": false, + "id": "NssF2iYfl_Bnw_ExbSTN9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 5.903783783236146, + "x": 738.4366388875179, + "y": 102.0454511378703, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 188.03873574733734, + "height": 38.94856770833333, + "seed": 1513132976, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721979493173, + "link": null, + "locked": false, + "fontSize": 15.579427083333332, + "fontFamily": 1, + "text": "If proposal submitted,\nthen owner = gov module", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "If proposal submitted,\nthen owner = gov module", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 3860, + "versionNonce": 1375313744, + "index": "ab", + "isDeleted": false, + "id": "SP-m3QTzlcLoU-_bmfZeN", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 542.8449540584817, + "y": 65.3169816199927, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 107.2195285426966, + "height": 95.35691573040356, + "seed": 1371342672, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721979518514, + "link": null, + "locked": false, + "startBinding": { + "elementId": "_VXIQppCSNb6MIFQLmDdy", + "focus": -1.0544694371314034, + "gap": 3.5954323396749857 + }, + "endBinding": { + "elementId": "_VXIQppCSNb6MIFQLmDdy", + "focus": 0.31663537700287836, + "gap": 1.9911615284421913 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 107.2195285426966, + -95.35691573040356 + ], + [ + 82.94381595386449, + -7.418196168042471 + ] + ] + }, + { + "type": "text", + "version": 1314, + "versionNonce": 863552848, + "index": "ac", + "isDeleted": false, + "id": "apwBA2F8OsZLAT7p6l1JS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 609.4229227304459, + "y": -79.25318690708701, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 569.839707493782, + "height": 40, + "seed": 1979332528, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721979490056, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "If proposal fails,\nthen owner = previous owner (the one that signed MsgRegisterConsumer)", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "If proposal fails,\nthen owner = previous owner (the one that signed MsgRegisterConsumer)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 772, + "versionNonce": 266696112, + "index": "ae", + "isDeleted": false, + "id": "pwlxDMNk9SimSRoZRuyV3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 256.2591145833333, + "y": 1317.2654695056733, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1658.3805338541667, + "height": 0.078125, + "seed": 1129032624, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 1658.3805338541667, + 0.078125 + ] + ] + }, + { + "type": "line", + "version": 379, + "versionNonce": 957038512, + "index": "af", + "isDeleted": false, + "id": "-xwyxjAJM89xAkUi-pWLB", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 269.35481770833337, + "y": 1261.0252351306735, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 659246416, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "line", + "version": 552, + "versionNonce": 250071472, + "index": "ag", + "isDeleted": false, + "id": "AFDJ07skaLW7VCALYMDj4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 760.7334914470289, + "y": 1257.3100290018028, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 1945500592, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "line", + "version": 835, + "versionNonce": 1285755824, + "index": "ah", + "isDeleted": false, + "id": "hDPzA0-vpS0ilSkphkeAA", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1002.3254193314633, + "y": 1269.644565149432, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 1257497936, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "arrow", + "version": 627, + "versionNonce": 140844464, + "index": "ai", + "isDeleted": false, + "id": "5RtUeYj4dxxweAu88pxEc", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1824.390625, + "y": 1297.1385163806735, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 88.623046875, + "height": 0, + "seed": 557184848, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 88.623046875, + 0 + ] + ] + }, + { + "type": "text", + "version": 694, + "versionNonce": 1398138800, + "index": "aj", + "isDeleted": false, + "id": "TCPMJfrcd92gSR4Q6cE6S", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1845.7822265625, + "y": 1273.2810945056735, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 40.63996636867523, + "height": 25, + "seed": 1234376112, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "time", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "time", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 653, + "versionNonce": 1503490480, + "index": "ak", + "isDeleted": false, + "id": "cJmAKusziodsDR6vwTHJd", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 213.76562500000006, + "y": 1198.4617585681735, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 227.99981439113617, + "height": 50, + "seed": 844025168, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "MsgRegisterConsumer\nto get the consumer id", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgRegisterConsumer\nto get the consumer id", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 717, + "versionNonce": 40090544, + "index": "al", + "isDeleted": false, + "id": "GastODl-l38mFb5oaAEbj", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 665.4558410644531, + "y": 1198.011929466611, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 210.61980283260345, + "height": 50, + "seed": 44005200, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "MsgInitializeConsumer\nwith spawnTime, etc.", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgInitializeConsumer\nwith spawnTime, etc.", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 801, + "versionNonce": 265340336, + "index": "am", + "isDeleted": false, + "id": "kXfBgGvtmnfZCVwG1uG9V", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 935.2071304321289, + "y": 1199.574429466611, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 224.29981744289398, + "height": 50, + "seed": 2087774640, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "MsgUpdateConsumer\nto set power cap, etc.", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgUpdateConsumer\nto set power cap, etc.", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 966, + "versionNonce": 1057787824, + "index": "an", + "isDeleted": false, + "id": "E1QM7dtsigeuyGX9zwvE0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1220.2525985836983, + "y": 1202.250210716611, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 242.3997710943222, + "height": 50, + "seed": 608854864, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "MsgInitializeConsumer to\nmodify spawnTime", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgInitializeConsumer to\nmodify spawnTime", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 688, + "versionNonce": 1748965808, + "index": "ao", + "isDeleted": false, + "id": "Wm2pyI04tXykKntCeM85W", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1307.6379193314638, + "y": 1266.2113376103696, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 1967873456, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "rectangle", + "version": 657, + "versionNonce": 512779184, + "index": "ap", + "isDeleted": false, + "id": "pfUxn633PL7PLmo5UBP83", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 273.84472656250006, + "y": 1317.1248851957778, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "width": 480.92285156250006, + "height": 81.1279296875, + "seed": 394410832, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "ZxRHa3OTec0AyDpAifqQw" + } + ], + "updated": 1721980422671, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 421, + "versionNonce": 1344826800, + "index": "aq", + "isDeleted": false, + "id": "ZxRHa3OTec0AyDpAifqQw", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 429.95622801780706, + "y": 1345.1888500395278, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 168.699848651886, + "height": 25, + "seed": 1250610096, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Registered phase", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "pfUxn633PL7PLmo5UBP83", + "originalText": "Registered phase", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1004, + "versionNonce": 1714370480, + "index": "ar", + "isDeleted": false, + "id": "uVblxWqL6Msu5umb9eTvZ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 758.35302734375, + "y": 1318.302456810361, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "width": 660.1660156250001, + "height": 81.1279296875, + "seed": 229232464, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "s-LIv7Mo1IvDb89Gr0uSQ" + } + ], + "updated": 1721980422671, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 782, + "versionNonce": 1691140528, + "index": "as", + "isDeleted": false, + "id": "s-LIv7Mo1IvDb89Gr0uSQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1007.056105658412, + "y": 1346.366421654111, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 162.75985899567604, + "height": 25, + "seed": 1548969296, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Initialized phase", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "uVblxWqL6Msu5umb9eTvZ", + "originalText": "Initialized phase", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 863, + "versionNonce": 2067805008, + "index": "at", + "isDeleted": false, + "id": "zXqjQ2rE_UbSjEHOyMDw3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1422.1748893529827, + "y": 1318.9168052711786, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 618764720, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980681550, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "text", + "version": 1091, + "versionNonce": 1936463280, + "index": "au", + "isDeleted": false, + "id": "aVp4yECj00-fGLMn7sHnD", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1309.797184765339, + "y": 1450.599820091611, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 177.4198455810547, + "height": 75, + "seed": 1447447888, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422672, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "spawnTime passed\nand at least one\nvalidator opted in", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "spawnTime passed\nand at least one\nvalidator opted in", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 1091, + "versionNonce": 851304368, + "index": "ax", + "isDeleted": false, + "id": "s6qt0UIRPRSbMwetkDFg_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1609.481927216053, + "y": 1212.733609154111, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 202.41985833644867, + "height": 50, + "seed": 1340941744, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422672, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "MsgUpdateConsumer\nto change power cap", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgUpdateConsumer\nto change power cap", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 985, + "versionNonce": 2034657712, + "index": "ay", + "isDeleted": false, + "id": "RRDK6Jp-NdnWMWtYGDDli", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1745.1088831870024, + "y": 1272.6322796427069, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 154253232, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980422672, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "rectangle", + "version": 1016, + "versionNonce": 841855920, + "index": "az", + "isDeleted": false, + "id": "4ZMVPnrnF1Dfj8ono67tv", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1425.8107096354167, + "y": 1320.40939512673, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 475.03417968750034, + "height": 79.30663117976157, + "seed": 66445648, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "KC5zECj3By5dDU8JFhAep" + } + ], + "updated": 1721980422672, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 810, + "versionNonce": 597409200, + "index": "b00", + "isDeleted": false, + "id": "KC5zECj3By5dDU8JFhAep", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1587.763779918353, + "y": 1347.7777356509237, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 151.1280391216278, + "height": 24.569950131374355, + "seed": 323827536, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422672, + "link": null, + "locked": false, + "fontSize": 19.655960105099485, + "fontFamily": 1, + "text": "Launched phase", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "4ZMVPnrnF1Dfj8ono67tv", + "originalText": "Launched phase", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 771, + "versionNonce": 1263014832, + "index": "b01", + "isDeleted": false, + "id": "7Bq_OzmNwUc0GxjMxTYNY", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 265.25883719345677, + "y": 1666.5525788806733, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1658.3805338541667, + "height": 0.078125, + "seed": 255347024, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 1658.3805338541667, + 0.078125 + ] + ] + }, + { + "type": "line", + "version": 400, + "versionNonce": 1785339312, + "index": "b02", + "isDeleted": false, + "id": "OvebgcABs_rLQivf-TBk2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 278.3545403184568, + "y": 1610.3123445056733, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 1510121296, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "line", + "version": 573, + "versionNonce": 750446512, + "index": "b03", + "isDeleted": false, + "id": "4C8GcmxoE4N2CEFpbetdn", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 769.7332140571524, + "y": 1606.5971383768026, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 1158821200, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "line", + "version": 856, + "versionNonce": 1046639024, + "index": "b04", + "isDeleted": false, + "id": "nHS8CykAzRlhhatMaNhMu", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1011.3251419415867, + "y": 1618.931674524432, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 364841808, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "arrow", + "version": 626, + "versionNonce": 109624240, + "index": "b05", + "isDeleted": false, + "id": "Fs9vTDdorvwlcYw_ubAa_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1833.3903476101234, + "y": 1646.4256257556733, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 88.623046875, + "height": 0, + "seed": 643899728, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 88.623046875, + 0 + ] + ] + }, + { + "type": "text", + "version": 715, + "versionNonce": 1196803504, + "index": "b06", + "isDeleted": false, + "id": "l3WpHc1Tm8DmkYOar2yuc", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1854.7819491726234, + "y": 1622.5682038806733, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 40.63996636867523, + "height": 25, + "seed": 1179678544, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "time", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "time", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 674, + "versionNonce": 1039241136, + "index": "b07", + "isDeleted": false, + "id": "HVWEVkdCna3gjxJRN-dCZ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 222.7653476101235, + "y": 1547.7488679431735, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 227.99981439113617, + "height": 50, + "seed": 434441552, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "MsgRegisterConsumer\nto get the consumer id", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgRegisterConsumer\nto get the consumer id", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 877, + "versionNonce": 1115953584, + "index": "b08", + "isDeleted": false, + "id": "jpeMZ5SS2jgAqO9FZB17B", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 672.7954074245766, + "y": 1496.999559674944, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 237.25982177257538, + "height": 100, + "seed": 2054611792, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "issue governance\nproposal with\nMsgInitializeConsumer\nand MsgUpdateconsumer", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "issue governance\nproposal with\nMsgInitializeConsumer\nand MsgUpdateconsumer", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 907, + "versionNonce": 1748329392, + "index": "b09", + "isDeleted": false, + "id": "zmw1PyM5vZzw3eZp9f19w", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 936.9607592922523, + "y": 1583.112840924944, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "width": 170.7798352241516, + "height": 25, + "seed": 1448999248, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "proposal rejected", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "proposal rejected", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1152, + "versionNonce": 489516464, + "index": "b0C", + "isDeleted": false, + "id": "ajHAAKi9tSoXcIkv3aA4L", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 277.09965750595677, + "y": 1665.9562654041108, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "width": 1646.6975911458337, + "height": 81.1279296875, + "seed": 948091728, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "2HQsEKAeWiKi5szJa8Io6" + } + ], + "updated": 1721980676778, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 916, + "versionNonce": 1251227568, + "index": "b0D", + "isDeleted": false, + "id": "2HQsEKAeWiKi5szJa8Io6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1016.0985287529306, + "y": 1694.0202302478608, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 168.699848651886, + "height": 25, + "seed": 355389776, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Registered phase", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ajHAAKi9tSoXcIkv3aA4L", + "originalText": "Registered phase", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1323, + "versionNonce": 515241808, + "index": "b0E", + "isDeleted": false, + "id": "o16fHdthOp80uqAN-KkL2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1015.8097812038734, + "y": 2025.532274518694, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "width": 415.0227864583335, + "height": 81.1279296875, + "seed": 1877485392, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "348R4_XQcWAs6CAl4OV_w" + } + ], + "updated": 1721980415621, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1102, + "versionNonce": 1234865488, + "index": "b0F", + "isDeleted": false, + "id": "348R4_XQcWAs6CAl4OV_w", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1141.9412449352021, + "y": 2053.596239362444, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 162.75985899567604, + "height": 25, + "seed": 352763216, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Initialized phase", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "o16fHdthOp80uqAN-KkL2", + "originalText": "Initialized phase", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 897, + "versionNonce": 394027856, + "index": "b0M", + "isDeleted": false, + "id": "jAITNaSlDtSPpvQwpBfRJ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 267.98783052464313, + "y": 2020.76074945359, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1658.3805338541667, + "height": 0.078125, + "seed": 124954448, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 1658.3805338541667, + 0.078125 + ] + ] + }, + { + "type": "line", + "version": 504, + "versionNonce": 917731664, + "index": "b0N", + "isDeleted": false, + "id": "akYH8jPOSYYHRJ0kHn6SF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 281.08353364964324, + "y": 1964.52051507859, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 1463893328, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "line", + "version": 677, + "versionNonce": 1251462992, + "index": "b0O", + "isDeleted": false, + "id": "Qds67yg8nIFlUKrTZNb8v", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 772.4622073883388, + "y": 1960.8053089497193, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 483201872, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "line", + "version": 960, + "versionNonce": 1266771280, + "index": "b0P", + "isDeleted": false, + "id": "jtzT1eH8RZn8QwNepmt_H", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1014.054135272773, + "y": 1973.1398450973488, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 778971472, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "arrow", + "version": 752, + "versionNonce": 1724398416, + "index": "b0Q", + "isDeleted": false, + "id": "8lhkFCwtbNTRFlmWeRE4n", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1836.1193409413095, + "y": 2000.63379632859, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 88.623046875, + "height": 0, + "seed": 1467776848, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 88.623046875, + 0 + ] + ] + }, + { + "type": "text", + "version": 819, + "versionNonce": 1589924176, + "index": "b0R", + "isDeleted": false, + "id": "CsZ965_KJ8mkVhd3JJHe6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1857.5109425038095, + "y": 1976.77637445359, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 40.63996636867523, + "height": 25, + "seed": 944772432, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "time", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "time", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 778, + "versionNonce": 1791862608, + "index": "b0S", + "isDeleted": false, + "id": "VptrXV13wo5wjJkN_HnlZ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 225.49434094130987, + "y": 1901.9570385160905, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 227.99981439113617, + "height": 50, + "seed": 641788752, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "MsgRegisterConsumer\nto get the consumer id", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgRegisterConsumer\nto get the consumer id", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 1003, + "versionNonce": 389823824, + "index": "b0T", + "isDeleted": false, + "id": "i7B5C0pGmcgSuCqx5vRQP", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 675.524400755763, + "y": 1851.207730247861, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 237.25982177257538, + "height": 100, + "seed": 195494224, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "issue governance\nproposal with\nMsgInitializeConsumer\nand MsgUpdateconsumer", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "issue governance\nproposal with\nMsgInitializeConsumer\nand MsgUpdateconsumer", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 1017, + "versionNonce": 623782224, + "index": "b0U", + "isDeleted": false, + "id": "KtxOQmQoRm5cuPDQBNvU5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 939.6897526234386, + "y": 1937.321011497861, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "width": 156.959858417511, + "height": 25, + "seed": 1205491536, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980635662, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "proposal passes", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "proposal passes", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1270, + "versionNonce": 1806248272, + "index": "b0X", + "isDeleted": false, + "id": "aqECH_oHGd4F7Pq9jBCpa", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 279.82865083714313, + "y": 2020.1644359770276, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "width": 731.2353515625001, + "height": 81.1279296875, + "seed": 2041829712, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "J8GsOJGu4wkpH9e3dq06b" + } + ], + "updated": 1721980415621, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1034, + "versionNonce": 894926672, + "index": "b0Y", + "isDeleted": false, + "id": "J8GsOJGu4wkpH9e3dq06b", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 561.0964022924502, + "y": 2048.2284008207776, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 168.699848651886, + "height": 25, + "seed": 244068176, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Registered phase", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "aqECH_oHGd4F7Pq9jBCpa", + "originalText": "Registered phase", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 991, + "versionNonce": 2012467536, + "index": "b0Z", + "isDeleted": false, + "id": "jepmnNxu-8rBXJjZKYZKQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1434.8150636276262, + "y": 2021.9498456357614, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 1374274896, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980684670, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "text", + "version": 1229, + "versionNonce": 57883811, + "index": "b0a", + "isDeleted": false, + "id": "prbzcGXJPRqkOVsHpdbs4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1321.581890289982, + "y": 2150.859422956194, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 176.8798370361328, + "height": 25, + "seed": 1092918096, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721986800613, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "spawnTime passed", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "spawnTime passed", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 1327, + "versionNonce": 1288511920, + "index": "b0b", + "isDeleted": false, + "id": "4_i6jup_6Cze3xommPSgL", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1494.9671535740297, + "y": 1916.495816185361, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 231.69981133937836, + "height": 50, + "seed": 1855238480, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980692762, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "MsgUpdateConsumer\nto change denylist, etc.", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgUpdateConsumer\nto change denylist, etc.", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 1250, + "versionNonce": 1557995440, + "index": "b0c", + "isDeleted": false, + "id": "G9GGcs9_A9FENDJhK85f5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1581.264682461645, + "y": 1974.519486673957, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 1636960080, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980691478, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "rectangle", + "version": 1141, + "versionNonce": 1680731472, + "index": "b0d", + "isDeleted": false, + "id": "TuSGExogIwVLdWKpZuLEE", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1437.5394255767267, + "y": 2023.904675074647, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 475.03417968750034, + "height": 79.30663117976157, + "seed": 1987353936, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Kha-a5kyL8kweb2LvlPtp" + } + ], + "updated": 1721980415621, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 936, + "versionNonce": 1218691920, + "index": "b0e", + "isDeleted": false, + "id": "Kha-a5kyL8kweb2LvlPtp", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1599.492495859663, + "y": 2051.2730155988406, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 151.1280391216278, + "height": 24.569950131374355, + "seed": 1049916240, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "fontSize": 19.655960105099485, + "fontFamily": 1, + "text": "Launched phase", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "TuSGExogIwVLdWKpZuLEE", + "originalText": "Launched phase", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 408, + "versionNonce": 396094800, + "index": "b0f", + "isDeleted": false, + "id": "UyHfZ2VJGqV1pDsuTD9cV", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": -108.94270833333303, + "y": 1338.8350902739019, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 341.5998840332031, + "height": 70, + "seed": 723455312, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721981143713, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "successful launch of an \nOpt In chain", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "successful launch of an \nOpt In chain", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 510, + "versionNonce": 1955709360, + "index": "b0g", + "isDeleted": false, + "id": "4n5NjjKLCJsk7c-7uAWhd", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": -50.64974466959666, + "y": 1661.4197256905686, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 263.73187255859375, + "height": 70, + "seed": 1223292336, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "failed launch of a \nTop N chain", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "failed launch of a \nTop N chain", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 577, + "versionNonce": 2112613712, + "index": "b0h", + "isDeleted": false, + "id": "vr9GdN-bw_UUQefiVts9a", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": -83.6536966959635, + "y": 2032.7738923572358, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 328.5238952636719, + "height": 70, + "seed": 1698232144, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980468104, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "successful launch of a \nTop N chain", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "successful launch of a \nTop N chain", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/docs/adrs/figures/adr19_flows_of_launching_a_consumer_chain.png b/docs/docs/adrs/figures/adr19_flows_of_launching_a_consumer_chain.png new file mode 100644 index 0000000000..72dffb2d67 Binary files /dev/null and b/docs/docs/adrs/figures/adr19_flows_of_launching_a_consumer_chain.png differ diff --git a/docs/docs/adrs/figures/adr19_phases_of_a_consumer_chain.excalidraw b/docs/docs/adrs/figures/adr19_phases_of_a_consumer_chain.excalidraw new file mode 100644 index 0000000000..4c4802ae69 --- /dev/null +++ b/docs/docs/adrs/figures/adr19_phases_of_a_consumer_chain.excalidraw @@ -0,0 +1,3404 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "ellipse", + "version": 432, + "versionNonce": 656013628, + "index": "a1", + "isDeleted": false, + "id": "SI0HtgaAMGQsMr1Y7bXrA", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 512.4765625, + "y": 248.29980468749991, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 143.13281250000014, + "height": 137.65234375000014, + "seed": 1039402003, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "A97toxLGgigcGTMSLzFRE" + }, + { + "id": "zeENeKW4p1ce_DWzf-29f", + "type": "arrow" + }, + { + "id": "qIMMSItT6zGcYO5ZUPcar", + "type": "arrow" + } + ], + "updated": 1721898441262, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 772, + "versionNonce": 276787644, + "index": "a1V", + "isDeleted": false, + "id": "A97toxLGgigcGTMSLzFRE", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 547.7299002905518, + "y": 306.9585237065766, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 72.41595458984375, + "height": 20, + "seed": 262978077, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898441262, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "initialized", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "SI0HtgaAMGQsMr1Y7bXrA", + "originalText": "initialized", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "ellipse", + "version": 225, + "versionNonce": 1063465660, + "index": "a2", + "isDeleted": false, + "id": "y5DOCeanki7NLMac-YZU_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 963.30078125, + "y": 252.48730468749994, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 113.27734375, + "height": 113.27734375, + "seed": 303821331, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "id": "zeENeKW4p1ce_DWzf-29f", + "type": "arrow" + }, + { + "type": "text", + "id": "_lR4nCPdco_VXP-hHHxFx" + }, + { + "id": "hoUATc742qsSXGtb3zXhk", + "type": "arrow" + }, + { + "id": "6m4pEWBrlMrOpXdR2qMdd", + "type": "arrow" + } + ], + "updated": 1721898441262, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 69, + "versionNonce": 241531708, + "index": "a2G", + "isDeleted": false, + "id": "_lR4nCPdco_VXP-hHHxFx", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 987.2738821091236, + "y": 299.07638760228764, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 65.23196411132812, + "height": 20, + "seed": 2116710579, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898441262, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "launched", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "y5DOCeanki7NLMac-YZU_", + "originalText": "launched", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 818, + "versionNonce": 352917936, + "index": "a4", + "isDeleted": false, + "id": "-Ezkh_kc6H-iZ8_N1SUwz", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 728.4853515625, + "y": 18.888671874999943, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 462.22369116544724, + "height": 40, + "seed": 176740147, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "JQNfOufG1Sc3Ln1iHc6v8", + "type": "arrow" + } + ], + "updated": 1721977797787, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "proposal with MsgInitializeConsumer & MsgUpdateConsumer\nto start a Top N chain", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "proposal with MsgInitializeConsumer & MsgUpdateConsumer\nto start a Top N chain", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 3380, + "versionNonce": 1334694320, + "index": "a5", + "isDeleted": false, + "id": "JQNfOufG1Sc3Ln1iHc6v8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 863.2801567822762, + "y": 66.26969410176584, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 208.7131111905685, + "height": 55.219229040456455, + "seed": 428750077, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721977807991, + "link": null, + "locked": false, + "startBinding": { + "elementId": "-Ezkh_kc6H-iZ8_N1SUwz", + "focus": 0.14349370214591656, + "gap": 7.381022226765893 + }, + "endBinding": { + "elementId": "_VXIQppCSNb6MIFQLmDdy", + "focus": 0.1824872496377905, + "gap": 2.2713741813428783 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -106.6058242763097, + 51.06445336557442 + ], + [ + -208.7131111905685, + 55.219229040456455 + ] + ] + }, + { + "type": "text", + "version": 1068, + "versionNonce": 1922126672, + "index": "a6", + "isDeleted": false, + "id": "9NwhWEx9olM4fARFt5hZe", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 189.74827575683594, + "y": -17.066406250000057, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 308.01581835746765, + "height": 80, + "seed": 304812115, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "NAUT1CeeaW6ntAtOKbXGS", + "type": "arrow" + } + ], + "updated": 1721977650326, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "tx to register\nan Opt In or Top N consumer chain\n(MsgRegisterConsumer)\nowner = signer of MsgRegisterConsumer", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "tx to register\nan Opt In or Top N consumer chain\n(MsgRegisterConsumer)\nowner = signer of MsgRegisterConsumer", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 331, + "versionNonce": 1834637060, + "index": "a7", + "isDeleted": false, + "id": "zeENeKW4p1ce_DWzf-29f", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 663.2551165518071, + "y": 319.75738423211857, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 299.31331302963645, + "height": 5.083241028410356, + "seed": 783684445, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898441294, + "link": null, + "locked": false, + "startBinding": { + "elementId": "9VJjnsFNvss8g8B_wchEr", + "focus": -1.1953607235764194, + "gap": 6.181092330381375 + }, + "endBinding": { + "elementId": "y5DOCeanki7NLMac-YZU_", + "focus": -0.08074296871041202, + "gap": 1.0000000000000995 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 299.31331302963645, + -5.083241028410356 + ] + ] + }, + { + "type": "text", + "version": 418, + "versionNonce": 1250834364, + "index": "a8", + "isDeleted": false, + "id": "9VJjnsFNvss8g8B_wchEr", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 669.0216369628906, + "y": 325.93847656249994, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 252.0958709716797, + "height": 60, + "seed": 1053753011, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "zeENeKW4p1ce_DWzf-29f", + "type": "arrow" + } + ], + "updated": 1721898441262, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "chain's spawnTime passed\nAND\nat least one validator opted in", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "chain's spawnTime passed\nAND\nat least one validator opted in", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 417, + "versionNonce": 7026096, + "index": "aF", + "isDeleted": false, + "id": "FgsFB8OrGtx1-nlYRY0Uz", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1100.889877319336, + "y": 155.14941406249994, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 229.75984984636307, + "height": 40, + "seed": 51301117, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "hoUATc742qsSXGtb3zXhk", + "type": "arrow" + } + ], + "updated": 1721979486389, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "MsgUpdateConsumer for both\nTop N and Opt In chains", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgUpdateConsumer for both\nTop N and Opt In chains", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 936, + "versionNonce": 1806394884, + "index": "aG", + "isDeleted": false, + "id": "hoUATc742qsSXGtb3zXhk", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1043.1909748738744, + "y": 254.20084659464027, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 81.51926732005495, + "height": 77.65472732322138, + "seed": 684481907, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898441295, + "link": null, + "locked": false, + "startBinding": { + "elementId": "y5DOCeanki7NLMac-YZU_", + "focus": -0.6276577497929622, + "gap": 3.005298194275724 + }, + "endBinding": { + "elementId": "y5DOCeanki7NLMac-YZU_", + "focus": 0.6500438788511836, + "gap": 6.714013400352123 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 81.51926732005495, + -47.95974594543503 + ], + [ + 34.8604592337133, + 29.69498137778635 + ] + ] + }, + { + "type": "ellipse", + "version": 356, + "versionNonce": 411466172, + "index": "aK", + "isDeleted": false, + "id": "U2EhanfFFSg1GNh4uQ9Xo", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 973.2587890625, + "y": 528.0859375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 113.27734375, + "height": 113.27734375, + "seed": 420095965, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "RfEXevpkB5rowEFa4mHDD" + }, + { + "id": "6m4pEWBrlMrOpXdR2qMdd", + "type": "arrow" + } + ], + "updated": 1721898441262, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 209, + "versionNonce": 294117948, + "index": "aL", + "isDeleted": false, + "id": "RfEXevpkB5rowEFa4mHDD", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 999.7359029831471, + "y": 574.6750204147877, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 60.22393798828125, + "height": 20, + "seed": 1217602621, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898441262, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "stopped", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "U2EhanfFFSg1GNh4uQ9Xo", + "originalText": "stopped", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 518, + "versionNonce": 1178907908, + "index": "aM", + "isDeleted": false, + "id": "6m4pEWBrlMrOpXdR2qMdd", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1027.530607098985, + "y": 367.313746063125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1.0811756614666592, + "height": 152.58658700381386, + "seed": 354868061, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898441295, + "link": null, + "locked": false, + "startBinding": { + "elementId": "y5DOCeanki7NLMac-YZU_", + "focus": -0.1267451224869414, + "gap": 2.042177966448442 + }, + "endBinding": { + "elementId": "U2EhanfFFSg1GNh4uQ9Xo", + "focus": -0.014589591451189068, + "gap": 8.198352788681127 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 1.0811756614666592, + 152.58658700381386 + ] + ] + }, + { + "type": "ellipse", + "version": 1043, + "versionNonce": 1743463856, + "index": "aP", + "isDeleted": false, + "id": "_VXIQppCSNb6MIFQLmDdy", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 527.1933593750001, + "y": 49.049845487915945, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 125.71093750000004, + "height": 126.94419050538774, + "seed": 1424716579, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "ND9wpVssAkaXAtA1CWR-j" + }, + { + "id": "qIMMSItT6zGcYO5ZUPcar", + "type": "arrow" + }, + { + "id": "JQNfOufG1Sc3Ln1iHc6v8", + "type": "arrow" + }, + { + "id": "NAUT1CeeaW6ntAtOKbXGS", + "type": "arrow" + }, + { + "id": "SP-m3QTzlcLoU-_bmfZeN", + "type": "arrow" + } + ], + "updated": 1721977737205, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1438, + "versionNonce": 303490108, + "index": "aQ", + "isDeleted": false, + "id": "ND9wpVssAkaXAtA1CWR-j", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 550.919332346884, + "y": 102.6403917713115, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 78.36793518066406, + "height": 20, + "seed": 704059075, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898441262, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "registered", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "_VXIQppCSNb6MIFQLmDdy", + "originalText": "registered", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 2498, + "versionNonce": 1811695536, + "index": "aR", + "isDeleted": false, + "id": "qIMMSItT6zGcYO5ZUPcar", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 598.616944950635, + "y": 175.68190954353736, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 95.2174182195338, + "height": 71.14063431860131, + "seed": 711212803, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721977823250, + "link": null, + "locked": false, + "startBinding": { + "elementId": "_VXIQppCSNb6MIFQLmDdy", + "focus": 0.7831196417981204, + "gap": 1 + }, + "endBinding": { + "elementId": "SI0HtgaAMGQsMr1Y7bXrA", + "focus": -0.9808503194655256, + "gap": 2.5866858268130386 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 93.66378808277591, + 56.75434794509022 + ], + [ + -1.5536301367578846, + 71.14063431860131 + ] + ] + }, + { + "type": "text", + "version": 894, + "versionNonce": 1163437392, + "index": "aS", + "isDeleted": false, + "id": "tONaGSodwA-i1Tr_ZvpZf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 703.2325744628906, + "y": 211.6659109933036, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 63.951934814453125, + "height": 40, + "seed": 1173975117, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721977821204, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "proposal\npasses", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "proposal\npasses", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 2698, + "versionNonce": 1501320528, + "index": "aW", + "isDeleted": false, + "id": "NAUT1CeeaW6ntAtOKbXGS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 295.2339916085824, + "y": 72.54770775067982, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 219.9931787394372, + "height": 44.86816430307441, + "seed": 713565901, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721977650641, + "link": null, + "locked": false, + "startBinding": { + "elementId": "9NwhWEx9olM4fARFt5hZe", + "focus": 0.5220455825778929, + "gap": 9.614114000679876 + }, + "endBinding": { + "elementId": "_VXIQppCSNb6MIFQLmDdy", + "focus": -0.040896091175469475, + "gap": 12.037638560264035 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 49.778852818030145, + 44.86816430307441 + ], + [ + 219.9931787394372, + 43.27181702781314 + ] + ] + }, + { + "type": "arrow", + "version": 2638, + "versionNonce": 2034843580, + "index": "aX", + "isDeleted": false, + "id": "QJHjyYDxam9jq3ifrE6rE", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 553.4126684100163, + "y": 172.7898695168439, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 88.24899059693328, + "height": 80.70767694042144, + "seed": 901908284, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898441262, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -87.03045455607186, + 30.651422159720436 + ], + [ + 1.218536040861423, + 80.70767694042144 + ] + ] + }, + { + "type": "text", + "version": 954, + "versionNonce": 1840703408, + "index": "aY", + "isDeleted": false, + "id": "OSTXsQ9rLudhM8FpW1Tmi", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 237.52793884277344, + "y": 181.43050856817348, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 220.76788437366486, + "height": 60, + "seed": 1590512260, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721981417956, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "tx to initialize a registered\nOpt In chain\n(MsgInitializeConsumer)", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "tx to initialize a registered\nOpt In chain\n(MsgInitializeConsumer)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 618, + "versionNonce": 1408338864, + "index": "aZ", + "isDeleted": false, + "id": "YfLjGiz8pwcgaeYLbqk17", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1046.5860595703125, + "y": 407.42009190150674, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 198.86389088630676, + "height": 40, + "seed": 200186756, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721977871422, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "MsgRemoveConsumer for\nTop N and Opt In chains", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgRemoveConsumer for\nTop N and Opt In chains", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 1258, + "versionNonce": 1493934000, + "index": "aa", + "isDeleted": false, + "id": "NssF2iYfl_Bnw_ExbSTN9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 5.903783783236146, + "x": 738.4366388875179, + "y": 102.0454511378703, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 188.03873574733734, + "height": 38.94856770833333, + "seed": 1513132976, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721979493173, + "link": null, + "locked": false, + "fontSize": 15.579427083333332, + "fontFamily": 1, + "text": "If proposal submitted,\nthen owner = gov module", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "If proposal submitted,\nthen owner = gov module", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 3860, + "versionNonce": 1375313744, + "index": "ab", + "isDeleted": false, + "id": "SP-m3QTzlcLoU-_bmfZeN", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 542.8449540584817, + "y": 65.3169816199927, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 107.2195285426966, + "height": 95.35691573040356, + "seed": 1371342672, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721979518514, + "link": null, + "locked": false, + "startBinding": { + "elementId": "_VXIQppCSNb6MIFQLmDdy", + "focus": -1.0544694371314034, + "gap": 3.5954323396749857 + }, + "endBinding": { + "elementId": "_VXIQppCSNb6MIFQLmDdy", + "focus": 0.31663537700287836, + "gap": 1.9911615284421913 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 107.2195285426966, + -95.35691573040356 + ], + [ + 82.94381595386449, + -7.418196168042471 + ] + ] + }, + { + "type": "text", + "version": 1314, + "versionNonce": 863552848, + "index": "ac", + "isDeleted": false, + "id": "apwBA2F8OsZLAT7p6l1JS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 609.4229227304459, + "y": -79.25318690708701, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 569.839707493782, + "height": 40, + "seed": 1979332528, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721979490056, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "If proposal fails,\nthen owner = previous owner (the one that signed MsgRegisterConsumer)", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "If proposal fails,\nthen owner = previous owner (the one that signed MsgRegisterConsumer)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "pwlxDMNk9SimSRoZRuyV3", + "type": "line", + "x": 256.2591145833333, + "y": 1317.2654695056733, + "width": 1658.3805338541667, + "height": 0.078125, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ae", + "roundness": { + "type": 2 + }, + "seed": 1129032624, + "version": 772, + "versionNonce": 266696112, + "isDeleted": false, + "boundElements": null, + "updated": 1721980422671, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1658.3805338541667, + 0.078125 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "id": "-xwyxjAJM89xAkUi-pWLB", + "type": "line", + "x": 269.35481770833337, + "y": 1261.0252351306735, + "width": 0, + "height": 124.8681640625, + "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": 659246416, + "version": 379, + "versionNonce": 957038512, + "isDeleted": false, + "boundElements": null, + "updated": 1721980422671, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "type": "line", + "version": 552, + "versionNonce": 250071472, + "index": "ag", + "isDeleted": false, + "id": "AFDJ07skaLW7VCALYMDj4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 760.7334914470289, + "y": 1257.3100290018028, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 1945500592, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "line", + "version": 835, + "versionNonce": 1285755824, + "index": "ah", + "isDeleted": false, + "id": "hDPzA0-vpS0ilSkphkeAA", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1002.3254193314633, + "y": 1269.644565149432, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 1257497936, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "id": "5RtUeYj4dxxweAu88pxEc", + "type": "arrow", + "x": 1824.390625, + "y": 1297.1385163806735, + "width": 88.623046875, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ai", + "roundness": { + "type": 2 + }, + "seed": 557184848, + "version": 627, + "versionNonce": 140844464, + "isDeleted": false, + "boundElements": null, + "updated": 1721980422671, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 88.623046875, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "TCPMJfrcd92gSR4Q6cE6S", + "type": "text", + "x": 1845.7822265625, + "y": 1273.2810945056735, + "width": 40.63996636867523, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aj", + "roundness": null, + "seed": 1234376112, + "version": 694, + "versionNonce": 1398138800, + "isDeleted": false, + "boundElements": null, + "updated": 1721980422671, + "link": null, + "locked": false, + "text": "time", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "time", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "cJmAKusziodsDR6vwTHJd", + "type": "text", + "x": 213.76562500000006, + "y": 1198.4617585681735, + "width": 227.99981439113617, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ak", + "roundness": null, + "seed": 844025168, + "version": 653, + "versionNonce": 1503490480, + "isDeleted": false, + "boundElements": null, + "updated": 1721980422671, + "link": null, + "locked": false, + "text": "MsgRegisterConsumer\nto get the consumer id", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgRegisterConsumer\nto get the consumer id", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 717, + "versionNonce": 40090544, + "index": "al", + "isDeleted": false, + "id": "GastODl-l38mFb5oaAEbj", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 665.4558410644531, + "y": 1198.011929466611, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 210.61980283260345, + "height": 50, + "seed": 44005200, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "MsgInitializeConsumer\nwith spawnTime, etc.", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgInitializeConsumer\nwith spawnTime, etc.", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 801, + "versionNonce": 265340336, + "index": "am", + "isDeleted": false, + "id": "kXfBgGvtmnfZCVwG1uG9V", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 935.2071304321289, + "y": 1199.574429466611, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 224.29981744289398, + "height": 50, + "seed": 2087774640, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "MsgUpdateConsumer\nto set power cap, etc.", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgUpdateConsumer\nto set power cap, etc.", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 966, + "versionNonce": 1057787824, + "index": "an", + "isDeleted": false, + "id": "E1QM7dtsigeuyGX9zwvE0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1220.2525985836983, + "y": 1202.250210716611, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 242.3997710943222, + "height": 50, + "seed": 608854864, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "MsgInitializeConsumer to\nmodify spawnTime", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgInitializeConsumer to\nmodify spawnTime", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 688, + "versionNonce": 1748965808, + "index": "ao", + "isDeleted": false, + "id": "Wm2pyI04tXykKntCeM85W", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1307.6379193314638, + "y": 1266.2113376103696, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 1967873456, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "id": "pfUxn633PL7PLmo5UBP83", + "type": "rectangle", + "x": 273.84472656250006, + "y": 1317.1248851957778, + "width": 480.92285156250006, + "height": 81.1279296875, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "groupIds": [], + "frameId": null, + "index": "ap", + "roundness": { + "type": 3 + }, + "seed": 394410832, + "version": 657, + "versionNonce": 512779184, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "ZxRHa3OTec0AyDpAifqQw" + } + ], + "updated": 1721980422671, + "link": null, + "locked": false + }, + { + "id": "ZxRHa3OTec0AyDpAifqQw", + "type": "text", + "x": 429.95622801780706, + "y": 1345.1888500395278, + "width": 168.699848651886, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "groupIds": [], + "frameId": null, + "index": "aq", + "roundness": null, + "seed": 1250610096, + "version": 421, + "versionNonce": 1344826800, + "isDeleted": false, + "boundElements": null, + "updated": 1721980422671, + "link": null, + "locked": false, + "text": "Registered phase", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "pfUxn633PL7PLmo5UBP83", + "originalText": "Registered phase", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1004, + "versionNonce": 1714370480, + "index": "ar", + "isDeleted": false, + "id": "uVblxWqL6Msu5umb9eTvZ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 758.35302734375, + "y": 1318.302456810361, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "width": 660.1660156250001, + "height": 81.1279296875, + "seed": 229232464, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "s-LIv7Mo1IvDb89Gr0uSQ" + } + ], + "updated": 1721980422671, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 782, + "versionNonce": 1691140528, + "index": "as", + "isDeleted": false, + "id": "s-LIv7Mo1IvDb89Gr0uSQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1007.056105658412, + "y": 1346.366421654111, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 162.75985899567604, + "height": 25, + "seed": 1548969296, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422671, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Initialized phase", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "uVblxWqL6Msu5umb9eTvZ", + "originalText": "Initialized phase", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 863, + "versionNonce": 2067805008, + "index": "at", + "isDeleted": false, + "id": "zXqjQ2rE_UbSjEHOyMDw3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1422.1748893529827, + "y": 1318.9168052711786, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 618764720, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980681550, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "text", + "version": 1091, + "versionNonce": 1936463280, + "index": "au", + "isDeleted": false, + "id": "aVp4yECj00-fGLMn7sHnD", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1309.797184765339, + "y": 1450.599820091611, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 177.4198455810547, + "height": 75, + "seed": 1447447888, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422672, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "spawnTime passed\nand at least one\nvalidator opted in", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "spawnTime passed\nand at least one\nvalidator opted in", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 1091, + "versionNonce": 851304368, + "index": "ax", + "isDeleted": false, + "id": "s6qt0UIRPRSbMwetkDFg_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1609.481927216053, + "y": 1212.733609154111, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 202.41985833644867, + "height": 50, + "seed": 1340941744, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422672, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "MsgUpdateConsumer\nto change power cap", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgUpdateConsumer\nto change power cap", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 985, + "versionNonce": 2034657712, + "index": "ay", + "isDeleted": false, + "id": "RRDK6Jp-NdnWMWtYGDDli", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1745.1088831870024, + "y": 1272.6322796427069, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 154253232, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980422672, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "rectangle", + "version": 1016, + "versionNonce": 841855920, + "index": "az", + "isDeleted": false, + "id": "4ZMVPnrnF1Dfj8ono67tv", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1425.8107096354167, + "y": 1320.40939512673, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 475.03417968750034, + "height": 79.30663117976157, + "seed": 66445648, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "KC5zECj3By5dDU8JFhAep" + } + ], + "updated": 1721980422672, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 810, + "versionNonce": 597409200, + "index": "b00", + "isDeleted": false, + "id": "KC5zECj3By5dDU8JFhAep", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1587.763779918353, + "y": 1347.7777356509237, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 151.1280391216278, + "height": 24.569950131374355, + "seed": 323827536, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980422672, + "link": null, + "locked": false, + "fontSize": 19.655960105099485, + "fontFamily": 1, + "text": "Launched phase", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "4ZMVPnrnF1Dfj8ono67tv", + "originalText": "Launched phase", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 771, + "versionNonce": 1263014832, + "index": "b01", + "isDeleted": false, + "id": "7Bq_OzmNwUc0GxjMxTYNY", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 265.25883719345677, + "y": 1666.5525788806733, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1658.3805338541667, + "height": 0.078125, + "seed": 255347024, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 1658.3805338541667, + 0.078125 + ] + ] + }, + { + "type": "line", + "version": 400, + "versionNonce": 1785339312, + "index": "b02", + "isDeleted": false, + "id": "OvebgcABs_rLQivf-TBk2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 278.3545403184568, + "y": 1610.3123445056733, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 1510121296, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "line", + "version": 573, + "versionNonce": 750446512, + "index": "b03", + "isDeleted": false, + "id": "4C8GcmxoE4N2CEFpbetdn", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 769.7332140571524, + "y": 1606.5971383768026, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 1158821200, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "line", + "version": 856, + "versionNonce": 1046639024, + "index": "b04", + "isDeleted": false, + "id": "nHS8CykAzRlhhatMaNhMu", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1011.3251419415867, + "y": 1618.931674524432, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 364841808, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "arrow", + "version": 626, + "versionNonce": 109624240, + "index": "b05", + "isDeleted": false, + "id": "Fs9vTDdorvwlcYw_ubAa_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1833.3903476101234, + "y": 1646.4256257556733, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 88.623046875, + "height": 0, + "seed": 643899728, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 88.623046875, + 0 + ] + ] + }, + { + "type": "text", + "version": 715, + "versionNonce": 1196803504, + "index": "b06", + "isDeleted": false, + "id": "l3WpHc1Tm8DmkYOar2yuc", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1854.7819491726234, + "y": 1622.5682038806733, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 40.63996636867523, + "height": 25, + "seed": 1179678544, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "time", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "time", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 674, + "versionNonce": 1039241136, + "index": "b07", + "isDeleted": false, + "id": "HVWEVkdCna3gjxJRN-dCZ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 222.7653476101235, + "y": 1547.7488679431735, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 227.99981439113617, + "height": 50, + "seed": 434441552, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "MsgRegisterConsumer\nto get the consumer id", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgRegisterConsumer\nto get the consumer id", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 877, + "versionNonce": 1115953584, + "index": "b08", + "isDeleted": false, + "id": "jpeMZ5SS2jgAqO9FZB17B", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 672.7954074245766, + "y": 1496.999559674944, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 237.25982177257538, + "height": 100, + "seed": 2054611792, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "issue governance\nproposal with\nMsgInitializeConsumer\nand MsgUpdateconsumer", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "issue governance\nproposal with\nMsgInitializeConsumer\nand MsgUpdateconsumer", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 907, + "versionNonce": 1748329392, + "index": "b09", + "isDeleted": false, + "id": "zmw1PyM5vZzw3eZp9f19w", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 936.9607592922523, + "y": 1583.112840924944, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "width": 170.7798352241516, + "height": 25, + "seed": 1448999248, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "proposal rejected", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "proposal rejected", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1152, + "versionNonce": 489516464, + "index": "b0C", + "isDeleted": false, + "id": "ajHAAKi9tSoXcIkv3aA4L", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 277.09965750595677, + "y": 1665.9562654041108, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "width": 1646.6975911458337, + "height": 81.1279296875, + "seed": 948091728, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "2HQsEKAeWiKi5szJa8Io6" + } + ], + "updated": 1721980676778, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 916, + "versionNonce": 1251227568, + "index": "b0D", + "isDeleted": false, + "id": "2HQsEKAeWiKi5szJa8Io6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1016.0985287529306, + "y": 1694.0202302478608, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 168.699848651886, + "height": 25, + "seed": 355389776, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Registered phase", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ajHAAKi9tSoXcIkv3aA4L", + "originalText": "Registered phase", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1323, + "versionNonce": 515241808, + "index": "b0E", + "isDeleted": false, + "id": "o16fHdthOp80uqAN-KkL2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1015.8097812038734, + "y": 2025.532274518694, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "width": 415.0227864583335, + "height": 81.1279296875, + "seed": 1877485392, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "348R4_XQcWAs6CAl4OV_w" + } + ], + "updated": 1721980415621, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1102, + "versionNonce": 1234865488, + "index": "b0F", + "isDeleted": false, + "id": "348R4_XQcWAs6CAl4OV_w", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1141.9412449352021, + "y": 2053.596239362444, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 162.75985899567604, + "height": 25, + "seed": 352763216, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Initialized phase", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "o16fHdthOp80uqAN-KkL2", + "originalText": "Initialized phase", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 897, + "versionNonce": 394027856, + "index": "b0M", + "isDeleted": false, + "id": "jAITNaSlDtSPpvQwpBfRJ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 267.98783052464313, + "y": 2020.76074945359, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1658.3805338541667, + "height": 0.078125, + "seed": 124954448, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 1658.3805338541667, + 0.078125 + ] + ] + }, + { + "type": "line", + "version": 504, + "versionNonce": 917731664, + "index": "b0N", + "isDeleted": false, + "id": "akYH8jPOSYYHRJ0kHn6SF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 281.08353364964324, + "y": 1964.52051507859, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 1463893328, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "line", + "version": 677, + "versionNonce": 1251462992, + "index": "b0O", + "isDeleted": false, + "id": "Qds67yg8nIFlUKrTZNb8v", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 772.4622073883388, + "y": 1960.8053089497193, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 483201872, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "line", + "version": 960, + "versionNonce": 1266771280, + "index": "b0P", + "isDeleted": false, + "id": "jtzT1eH8RZn8QwNepmt_H", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1014.054135272773, + "y": 1973.1398450973488, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 778971472, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "arrow", + "version": 752, + "versionNonce": 1724398416, + "index": "b0Q", + "isDeleted": false, + "id": "8lhkFCwtbNTRFlmWeRE4n", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1836.1193409413095, + "y": 2000.63379632859, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 88.623046875, + "height": 0, + "seed": 1467776848, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 88.623046875, + 0 + ] + ] + }, + { + "type": "text", + "version": 819, + "versionNonce": 1589924176, + "index": "b0R", + "isDeleted": false, + "id": "CsZ965_KJ8mkVhd3JJHe6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1857.5109425038095, + "y": 1976.77637445359, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 40.63996636867523, + "height": 25, + "seed": 944772432, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "time", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "time", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 778, + "versionNonce": 1791862608, + "index": "b0S", + "isDeleted": false, + "id": "VptrXV13wo5wjJkN_HnlZ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 225.49434094130987, + "y": 1901.9570385160905, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 227.99981439113617, + "height": 50, + "seed": 641788752, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "MsgRegisterConsumer\nto get the consumer id", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgRegisterConsumer\nto get the consumer id", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 1003, + "versionNonce": 389823824, + "index": "b0T", + "isDeleted": false, + "id": "i7B5C0pGmcgSuCqx5vRQP", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 675.524400755763, + "y": 1851.207730247861, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 237.25982177257538, + "height": 100, + "seed": 195494224, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "issue governance\nproposal with\nMsgInitializeConsumer\nand MsgUpdateconsumer", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "issue governance\nproposal with\nMsgInitializeConsumer\nand MsgUpdateconsumer", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 1017, + "versionNonce": 623782224, + "index": "b0U", + "isDeleted": false, + "id": "KtxOQmQoRm5cuPDQBNvU5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 939.6897526234386, + "y": 1937.321011497861, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "width": 156.959858417511, + "height": 25, + "seed": 1205491536, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980635662, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "proposal passes", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "proposal passes", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1270, + "versionNonce": 1806248272, + "index": "b0X", + "isDeleted": false, + "id": "aqECH_oHGd4F7Pq9jBCpa", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 279.82865083714313, + "y": 2020.1644359770276, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e7f5ff", + "width": 731.2353515625001, + "height": 81.1279296875, + "seed": 2041829712, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "J8GsOJGu4wkpH9e3dq06b" + } + ], + "updated": 1721980415621, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1034, + "versionNonce": 894926672, + "index": "b0Y", + "isDeleted": false, + "id": "J8GsOJGu4wkpH9e3dq06b", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 561.0964022924502, + "y": 2048.2284008207776, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 168.699848651886, + "height": 25, + "seed": 244068176, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Registered phase", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "aqECH_oHGd4F7Pq9jBCpa", + "originalText": "Registered phase", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 991, + "versionNonce": 2012467536, + "index": "b0Z", + "isDeleted": false, + "id": "jepmnNxu-8rBXJjZKYZKQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1434.8150636276262, + "y": 2021.9498456357614, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 1374274896, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980684670, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "text", + "version": 1224, + "versionNonce": 550960976, + "index": "b0a", + "isDeleted": false, + "id": "prbzcGXJPRqkOVsHpdbs4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1320.214702789982, + "y": 2151.3216625395276, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 177.4198455810547, + "height": 75, + "seed": 1092918096, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980686051, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "spawnTime passed\nand at least one\nvalidator opted in", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "spawnTime passed\nand at least one\nvalidator opted in", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 1327, + "versionNonce": 1288511920, + "index": "b0b", + "isDeleted": false, + "id": "4_i6jup_6Cze3xommPSgL", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1494.9671535740297, + "y": 1916.495816185361, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 231.69981133937836, + "height": 50, + "seed": 1855238480, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980692762, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "MsgUpdateConsumer\nto change denylist, etc.", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "MsgUpdateConsumer\nto change denylist, etc.", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 1250, + "versionNonce": 1557995440, + "index": "b0c", + "isDeleted": false, + "id": "G9GGcs9_A9FENDJhK85f5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1581.264682461645, + "y": 1974.519486673957, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 124.8681640625, + "seed": 1636960080, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721980691478, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 124.8681640625 + ] + ] + }, + { + "type": "rectangle", + "version": 1141, + "versionNonce": 1680731472, + "index": "b0d", + "isDeleted": false, + "id": "TuSGExogIwVLdWKpZuLEE", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1437.5394255767267, + "y": 2023.904675074647, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 475.03417968750034, + "height": 79.30663117976157, + "seed": 1987353936, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Kha-a5kyL8kweb2LvlPtp" + } + ], + "updated": 1721980415621, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 936, + "versionNonce": 1218691920, + "index": "b0e", + "isDeleted": false, + "id": "Kha-a5kyL8kweb2LvlPtp", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1599.492495859663, + "y": 2051.2730155988406, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 151.1280391216278, + "height": 24.569950131374355, + "seed": 1049916240, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980415621, + "link": null, + "locked": false, + "fontSize": 19.655960105099485, + "fontFamily": 1, + "text": "Launched phase", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "TuSGExogIwVLdWKpZuLEE", + "originalText": "Launched phase", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "UyHfZ2VJGqV1pDsuTD9cV", + "type": "text", + "x": -108.94270833333303, + "y": 1338.8350902739019, + "width": 341.5998840332031, + "height": 70, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "groupIds": [], + "frameId": null, + "index": "b0f", + "roundness": null, + "seed": 723455312, + "version": 408, + "versionNonce": 396094800, + "isDeleted": false, + "boundElements": null, + "updated": 1721981143713, + "link": null, + "locked": false, + "text": "successful launch of an \nOpt In chain", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "successful launch of an \nOpt In chain", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 510, + "versionNonce": 1955709360, + "index": "b0g", + "isDeleted": false, + "id": "4n5NjjKLCJsk7c-7uAWhd", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": -50.64974466959666, + "y": 1661.4197256905686, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 263.73187255859375, + "height": 70, + "seed": 1223292336, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980676778, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "failed launch of a \nTop N chain", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "failed launch of a \nTop N chain", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 577, + "versionNonce": 2112613712, + "index": "b0h", + "isDeleted": false, + "id": "vr9GdN-bw_UUQefiVts9a", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": -83.6536966959635, + "y": 2032.7738923572358, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 328.5238952636719, + "height": 70, + "seed": 1698232144, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721980468104, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "successful launch of a \nTop N chain", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "successful launch of a \nTop N chain", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "udO72dQy2VH8wQm_dcFbx", + "type": "line", + "x": 290.74479166666686, + "y": 2111.0854564848396, + "width": 156.04817708333314, + "height": 104.58333333333394, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 60, + "groupIds": [], + "frameId": null, + "index": "b0l", + "roundness": { + "type": 2 + }, + "seed": 1863593392, + "version": 301, + "versionNonce": 1529007952, + "isDeleted": false, + "boundElements": null, + "updated": 1721981217963, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 156.04817708333314, + 104.58333333333394 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null + }, + { + "type": "line", + "version": 598, + "versionNonce": 1734852016, + "index": "b0m", + "isDeleted": false, + "id": "zHhFHMZyTd9z3sepnFUy8", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 1012.4072684983414, + "y": 2107.4292076232887, + "strokeColor": "#e03131", + "backgroundColor": "#b2f2bb", + "width": 205.91796875000023, + "height": 134.21875, + "seed": 1304021840, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721981220095, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -205.91796875000023, + 134.21875 + ] + ] + }, + { + "id": "0kk2QokOsQCRZigvw5I2z", + "type": "text", + "x": 460.60807291666663, + "y": 2199.17139398484, + "width": 395.6397705078125, + "height": 140, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 60, + "groupIds": [], + "frameId": null, + "index": "b0n", + "roundness": null, + "seed": 1799721904, + "version": 219, + "versionNonce": 205377456, + "isDeleted": false, + "boundElements": null, + "updated": 1721981246158, + "link": null, + "locked": false, + "text": "NO MsgInitializeConsumer\nor MsgUpdateConsumer\nshould be executed as \ntransactions during this time", + "fontSize": 28, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "NO MsgInitializeConsumer\nor MsgUpdateConsumer\nshould be executed as \ntransactions during this time", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/docs/adrs/figures/adr19_phases_of_a_consumer_chain.png b/docs/docs/adrs/figures/adr19_phases_of_a_consumer_chain.png new file mode 100644 index 0000000000..ce6eb8c451 Binary files /dev/null and b/docs/docs/adrs/figures/adr19_phases_of_a_consumer_chain.png differ diff --git a/docs/docs/adrs/intro.md b/docs/docs/adrs/intro.md index a1dac24771..c1a494f99e 100644 --- a/docs/docs/adrs/intro.md +++ b/docs/docs/adrs/intro.md @@ -41,12 +41,14 @@ To suggest an ADR, please make use of the [ADR template](https://github.com/cosm - [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) +- [ADR 017: ICS with Inactive Provider Validators](./adr-017-allowing-inactive-validators.md) +- [ADR 018: Remove VSCMatured Packets](./adr-018-remove-vscmatured.md) +- [ADR 18: Permissionless Interchain Security](./adr-019-permissionless-ics.md) ### Proposed - [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/docs/consumer-development/onboarding.md b/docs/docs/consumer-development/onboarding.md index 9f438dd8d4..a86aa00aa9 100644 --- a/docs/docs/consumer-development/onboarding.md +++ b/docs/docs/consumer-development/onboarding.md @@ -119,7 +119,11 @@ Example of a consumer chain addition proposal. // the consumer chain. "allowlist": [], // Corresponds to a list of provider consensus addresses of validators that CANNOT validate the consumer chain. - "denylist": [] + "denylist": [], + // Corresponds to the minimal amount of (provider chain) stake required to validate on the consumer chain. + "min_stake": 0, + // Corresponds to whether inactive validators are allowed to validate the consumer chain. + "allow_inactive_vals": false } ``` diff --git a/docs/docs/features/democracy-modules.md b/docs/docs/features/democracy-modules.md index a89466d863..2c3ea2b2d5 100644 --- a/docs/docs/features/democracy-modules.md +++ b/docs/docs/features/democracy-modules.md @@ -6,7 +6,7 @@ sidebar_position: 5 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 democracy modules comprise `x/staking`, `x/distribution` and `x/governance` with overrides and extensions required for normal operation when participating in ICS. The modules are plug-and-play and only require small wiring changes to be enabled. @@ -16,40 +16,30 @@ For a full integration check the `consumer-democracy` [example app](https://gith 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. +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 validator set to the consensus engine. ### Implications for consumer chains -The `x/ccv/democracy/staking` allows consumer chains to separate governance from block production. - -:::info +The `x/ccv/democracy/staking` allows consumer chains to **_separate governance from block production_**. 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: +Validators registered with the `x/staking` module become __Governators__. +Unlike validators, governators are not required to run any chain infrastructure 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 +- 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 block rewards -- the block rewards kept on the consumer (see the [ConsumerRedistributionFraction param](../introduction/params.md#consumerredistributionfraction)) are distributed to all governators and their delegators. -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. +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. -::: +The consumer chain's token will remain a governance token. The token's parameters (inflation, max supply, burn rate) are completely under the control of the consumer chain. ### Integration @@ -82,7 +72,7 @@ To integrate the `democracy/staking` follow this guide: #### 1. confirm that no modules are returning validator updates -:::tip +:::warning Only the `x/ccv/consumer` module should be returning validator updates. ::: @@ -231,12 +221,8 @@ func NewApp(...) { ## 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 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. +As a result, consumer chains can limit the types of governance proposals that can be executed on chain to avoid inadvertent changes to the ICS protocol that could affect security properties. ### Integration @@ -453,23 +439,14 @@ func NewApp(...) { ## 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. -::: +The `democracy/distribution` module allows the consumer chain to send rewards to the provider chain while retaining the logic of the `x/distribution` module for internal reward distribution to governators and their delegators. ### 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. -::: +First, a percentage of the block rewards is sent to the provider chain, where is distributed only to opted-in validators and their delegators. +Second, the remaining rewards get distributed to the consumer chain's governators and their delegators. +The percentage that is sent to the provider chain corresponds to `1 - ConsumerRedistributionFraction`. +For example, `ConsumerRedistributionFraction = "0.75"` means that the consumer chain retains 75% of the rewards, while 25% gets sent to the provider chain ### Integration diff --git a/docs/docs/features/key-assignment.md b/docs/docs/features/key-assignment.md index 85dcb8e074..b9f1496dde 100644 --- a/docs/docs/features/key-assignment.md +++ b/docs/docs/features/key-assignment.md @@ -11,23 +11,22 @@ The feature is outlined in this [ADR-001](../adrs/adr-001-key-assignment.md) By sending an `AssignConsumerKey` transaction, validators are able to indicate which consensus key they will be using to validate a consumer chain. On receiving the transaction, if the key assignment is valid, the provider will use the assigned consensus key when it sends future voting power updates to the consumer that involve the validator. -:::tip -Key assignment is handled only by the provider chain - the consumer chains are not aware of the fact that different consensus keys represent the same validator entity. -::: +Note that key assignment is handled only by the provider chain - the consumer chains are not aware of the fact that different consensus keys represent the same validator entity. ## Rules -- a key can be assigned as soon as the consumer addition proposal is submitted to the provider -- validator A cannot assign consumer key K to consumer chain X if there is already a validator B (B!=A) using K on the provider -- validator A cannot assign consumer key K to consumer chain X if there is already a validator B using K on X -- a new validator on the provider cannot use a consensus key K if K is already used by any validator on any consumer chain - -:::tip -Validators can use a different key for each consumer chain. -::: +- A key can be assigned as soon as the consumer addition proposal is submitted to the provider. +- Validator `A` cannot assign consumer key `K` to consumer chain `X` if there is already a validator `B` (`B!=A`) using `K` on the provider. +- Validator `A` cannot assign consumer key `K` to consumer chain `X` if there is already a validator `B` using `K` on `X`. +- A new validator on the provider cannot use a consensus key `K` if `K` is already used by any validator on any consumer chain. ## Adding a 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. +::: + First, create a new node on the consumer chain using the equivalent: ```bash @@ -81,11 +80,6 @@ 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 4e221583b5..eadb841e59 100644 --- a/docs/docs/features/partial-set-security.md +++ b/docs/docs/features/partial-set-security.md @@ -4,30 +4,21 @@ sidebar_position: 6 # 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: +Partial Set Security (PSS) is a complete revamp of the Hub's security offering. It allows consumer chains to leverage only a subset of validators from the provider chain, which offers more flexibility than the previous 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. +An advantage of Top N chains is that they are guaranteed to receive at least a certain fraction of the market cap of the provider chain in security. +In turn, Top N chains need to be approved by governance, since some validators will be forced to validate on them. +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. +Opt-In chains, on the other hand, are more flexible. +Validators are never forced to validate these chains and simply opt in if they want to. +Because of this, Opt-In chains can be **_launch completely permissionlessly_** by sending a transaction to the provider chain. +As a trade-off though, 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. +Note that Top N consumer chains can become Opt-In chains or vice versa via a [`ConsumerModificationProposal`](./proposals.md#consumermodificationproposal). -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 +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. diff --git a/docs/docs/features/power-shaping.md b/docs/docs/features/power-shaping.md index 0b065c3ac0..169872b9b2 100644 --- a/docs/docs/features/power-shaping.md +++ b/docs/docs/features/power-shaping.md @@ -5,66 +5,88 @@ sidebar_position: 7 # Power Shaping To give consumer chains more flexibility in choosing their validator set, Interchain Security offers -several "power shaping" mechanisms for consumer chains. +several ways to shape the powers of the validator sets on the 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. 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 +## Power Shaping Configuration + +Currently, ICS supports the following power shaping parameters. + +### Capping the validator set size + +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. +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). -::: -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 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. -:::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. + +Note that this parameter only applies to Opt In consumer chains (i.e., with Top N = 0). + +### Capping the validator powers + +The consumer chain can specify a maximum fraction of the total voting power any of its validators should have. +This is a security measure that makes it harder for a single large validator to take over a consumer chain: +It mitigates the risk of an Opt In chain with only a few validators being dominated by a validator with a large amount of stake. +For example, setting this fraction to 33% would mean that no single validator can have more than 33% of the total voting power on the consumer, and thus no single validator would be able to stop the consumer by going offline. + +:::warning +This parameter 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. + +Note that rewards are distributed proportionally to validators with respect to their capped voting power on the consumer, not their total voting power on the provider. + +### 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. +By default, both lists are empty -- there are no restrictions on which validators are eligible to opt in. :::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, +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. +### Minimum validator stake -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 consumer chains can specify a minimum amount of stake that any validator must have on the provider chain to be eligible to opt in. +For example, setting this to 1000 would mean only validators with at least 1000 tokens staked on the provider chain can validate the consumer chain. -The values can be seen by querying the list of consumer chains: -```bash -interchain-security-pd query provider list-consumer-chains -``` +### Allow inactive validators -## Guidelines for setting power shaping parameters +The consumer chains can specify whether validators outside of the provider's active set are eligible to opt in. +This can be useful for chains that want to have a larger validator set than the provider chain, or for chains that want to have a more decentralized validator set. +Consumer chains that enable this feature should strongly consider setting a minimum validator stake to ensure that only validators with some reputation/stake can validate the chain. +By default, this parameter is set to `false`, i.e., validators outside of the provider's active set are not eligible to opt in. + +## Setting Power Shaping Parameters + +All the power shaping parameters can be set by the consumer chain in the `ConsumerAdditionProposal` (see [Onboarding](../consumer-development/onboarding.md#3-submit-a-governance-proposal)). +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. 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. + +* **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. +* **Be aware of the interaction between capping the validator powers capping the validator set size.** + 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). + Also note 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. +* **Do not have allowlist contain too few validators.** + If the allowlist is _non empty_, then _only_ validators on the allowlist can validate the chain. + Thus, an allowlist containing too few validators is a security risk, e.g., the validators on the allowlist get jailed on the provider. +* **Do not have denylist contain too many validators.** + If the denylist is *non empty*, then the validators on the denylist cannot validate the chain. + Thus, a denylist containing too many validators is a security risk, e.g., the validators on the denylist represents a large fraction of the provider's 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. -:::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 + +The power shaping parameters can be seen by querying the list of consumer chains: + +```bash +interchain-security-pd query provider list-consumer-chains +``` + diff --git a/docs/docs/features/proposals.md b/docs/docs/features/proposals.md index 44ea4f0298..2df217088e 100644 --- a/docs/docs/features/proposals.md +++ b/docs/docs/features/proposals.md @@ -2,21 +2,19 @@ sidebar_position: 3 --- - # ICS Provider Proposals -Interchain security module introduces new proposal types to the provider. - -The proposals are used to propose upcoming interchain security events through governance. +Interchain Security introduces the following new governance proposal types to the provider chain. ## `ConsumerAdditionProposal` -:::info + +:::tip If you are preparing a `ConsumerAdditionProposal` you can find more information in the [consumer onboarding checklist](../consumer-development/onboarding.md). ::: -Proposal type used to suggest adding a new consumer chain. - -When proposals of this type are passed and the `spawn_time` specified in the proposal is reached, all provider chain validators are expected to run infrastructure (validator nodes) for the proposed consumer chain. +`ConsumerAdditionProposal` is used to add new consumer chains. +When proposals of this type pass governance and the `spawn_time` specified in the proposal is reached, all opted in provider validators are expected to run infrastructure (validator nodes) for the proposed consumer chain. +Note that for TopN consumer chains, the validators in the top N% of the voting power are automatically opted in at spawn time. Minimal example: ```js @@ -48,22 +46,18 @@ Minimal example: "validators_power_cap": 0, "validator_set_cap": 0, "allowlist": [], - "denylist": [] + "denylist": [], + "min_stake": 0, + "allow_inactive_vals": false } ``` -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). -## `ConsumerRemovalProposal` -Proposal type used to suggest removing an existing consumer chain. +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). -When proposals of this type are passed, the consumer chain in question will be gracefully removed from interchain security and validators will no longer be required to run infrastructure for the specified chain. -After the consumer chain removal, the chain in question will no longer be secured by the provider's validator set. +## `ConsumerRemovalProposal` -:::info -The chain in question my continue to produce blocks, but the validator set can no longer be slashed for any infractions committed on that chain. -Additional steps are required to completely offboard a consumer chain, such as re-introducing the staking module and removing the provider's validators from the active set. -More information will be made available in the [Consumer Offboarding Checklist](../consumer-development/offboarding.md). -::: +`ConsumerRemovalProposal` is used to remove an existing consumer chain. +When proposals of this type pass governance, the consumer chain in question will be gracefully removed from Interchain Security and validators will no longer be required to run infrastructure for the specified chain. Minimal example: ```js @@ -77,15 +71,17 @@ Minimal example: } ``` +After the consumer chain removal, the consumer chain is no longer secured by the provider chain. +The consumer chain might continue to produce blocks using the last validator set received from the provider. +However, its validator set can no longer be slashed for any infractions committed on the consumer. +Additional steps are required to completely offboard a consumer chain, such as re-introducing the staking module and removing the provider's validators from the active set. + ## `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`. +`ConsumerModificationProposal` is 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 proposals of this type pass governance, the consumer chain in question 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. +For example, given `chain-1` is a TopN consumer chain with, 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", @@ -108,13 +104,10 @@ from 35 to 40, then the `ConsumerModificationProposal` would still need to inclu 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. +## `ChangeRewardDenomProposal` -:::tip -A `ChangeRewardDenomProposal` will only be accepted on the provider chain if at least one of the `denomsToAdd` or `denomsToRemove` fields is populated with at least one denom. Also, a denom cannot be repeated in both sets. -::: +`ChangeRewardDenomProposal` is used to update the set of denoms accepted by the provider as rewards. +Note that a `ChangeRewardDenomProposal` will only be accepted on the provider chain if at least one of the `denomsToAdd` or `denomsToRemove` fields is populated with at least one denom. Also, a denom cannot be repeated in both sets. Minimal example: ```js @@ -126,10 +119,8 @@ Minimal example: } ``` -:::tip Besides native provider denoms (e.g., `uatom` for the Cosmos Hub), please use the `ibc/*` denom trace format. -For example, for `untrn` transferred over the path `transfer/channel-569`, the denom trace -can be queried using the following command: +For example, for `untrn` transferred over the path `transfer/channel-569`, the denom trace can be queried using the following command: ```bash > gaiad query ibc-transfer denom-hash transfer/channel-569/untrn hash: 0025F8A87464A471E66B234C4F93AEC5B4DA3D42D7986451A059273426290DD5 @@ -143,4 +134,3 @@ Then use the resulting hash in the `ChangeRewardDenomProposal`, e.g., "denomsToRemove": [] } ``` -::: diff --git a/docs/docs/features/reward-distribution.md b/docs/docs/features/reward-distribution.md index e042605cdd..8535ebaca1 100644 --- a/docs/docs/features/reward-distribution.md +++ b/docs/docs/features/reward-distribution.md @@ -7,25 +7,23 @@ sidebar_position: 2 Sending and distributing rewards from consumer chains to the provider chain is handled by the [Reward Distribution sub-protocol](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#reward-distribution). -Consumer chains have the option of sharing (a portion of) their block rewards (inflation tokens and fees) with the provider chain validators and delegators. -In Interchain Security, block rewards are periodically sent from the consumer to the provider according to consumer chain parameters using an IBC transfer channel. -This channel is created during consumer chain initialization, unless it is provided via the `ConsumerAdditionProposal` when adding a new consumer chain. -For more details, see the [reward distribution parameters](../introduction/params.md#reward-distribution-parameters). +Consumer chains have the option of sharing _a portion of_ their block rewards (inflation tokens and fees) with the provider chain as ICS rewards. +These rewards are periodically sent from the consumer to the provider according to [consumer chain parameters](../introduction/params.md#reward-distribution-parameters) using an IBC transfer channel. +This channel is created during consumer chain initialization, unless it is provided when creating a new consumer chain (see the [DistributionTransmissionChannel param](../introduction/params.md#distributiontransmissionchannel)). -:::tip -Providing an IBC transfer channel (see `DistributionTransmissionChannel`) enables a consumer chain to re-use one of the existing channels to the provider for consumer chain rewards distribution. This will preserve the `ibc denom` that may already be in use. +Providing an IBC transfer channel enables a consumer chain to re-use one of the existing channels to the provider for consumer chain rewards distribution. +This will preserve the `ibc denom` that may already be in use. This is especially important for standalone chains transitioning to become consumer chains. For more details, see the [changeover procedure](../consumer-development/changeover-procedure.md). -::: -Reward distribution on the provider is handled by the distribution module. +Once on the provider, the ICS rewards are distributed to the opted in validators and their delegators. +To avoid spam, the provider must whitelist denoms before accepting them as ICS rewards. ## Whitelisting Reward Denoms The ICS distribution system works by allowing consumer chains to send rewards to a module address on the provider called the `ConsumerRewardsPool`. -To avoid spam, the provider must whitelist denoms before accepting them as ICS rewards. -Only whitelisted denoms are transferred from the `ConsumerRewardsPool` to the `FeePoolAddress`, to be distributed to delegators and validators. -The whitelisted denoms can be adjusted through governance by sending a [`ChangeRewardDenomProposal`](./proposals.md#changerewarddenomproposal). +Only whitelisted denoms are transferred from the `ConsumerRewardsPool` to the `FeePoolAddress`, to be distributed to validators and their delegators. +The whitelisted denoms can be adjusted through governance by sending a [`ChangeRewardDenomProposal`](./proposals.md#changerewarddenomproposal). To query the list of whitelisted reward denoms on the Cosmos Hub, use the following command: ```bash diff --git a/docs/docs/features/slashing.md b/docs/docs/features/slashing.md index 718c3cbad0..295e22cdca 100644 --- a/docs/docs/features/slashing.md +++ b/docs/docs/features/slashing.md @@ -3,27 +3,23 @@ sidebar_position: 4 --- # Consumer Initiated Slashing -A consumer chain is essentially a regular Cosmos-SDK based chain that uses the Interchain Security module to achieve economic security by stake deposited on the provider chain, instead of its own chain. -In essence, provider chain and consumer chains are different networks (different infrastructures) that are bound together by the provider's validator set. By being bound to the provider's validator set, a consumer chain inherits the economic security guarantees of the provider chain (in terms of total stake). +A consumer chain is essentially a regular Cosmos-SDK based chain that uses the ICS consumer module to achieve economic security by stake deposited on the provider chain, instead of its own chain. +In essence, the provider chain and consumer chains are different networks (different infrastructures) that share a subset of the provider's validator set. By being bound to the provider's validator set, a consumer chain inherits some of the economic security guarantees of the provider chain. To maintain the proof of stake model, the consumer chain is able to send evidence of infractions (double signing and downtime) to the provider chain so the offending validators can be penalized. Any infraction committed on any of the consumer chains is reflected on the provider and all other consumer chains. -In the current implementation there are two important changes brought by the Interchain Security module. +The ICS protocol differentiates between downtime and equivocation infractions. ## Downtime Infractions -Downtime infractions are reported by consumer chains and are acted upon on the provider as soon as the provider receives the infraction evidence. - -Instead of slashing, the provider will only jail offending validator for the duration of time established by the chain parameters. - -:::info -[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, +Downtime infractions are reported by consumer chains and are acted upon on the provider as soon as they are received. +Instead of slashing, the provider will **_only jail_** offending validator for the duration of time established by the provider chain parameters. +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. +For preventing malicious consumer chains from harming the provider, [slash throttling](../adrs/adr-002-throttle.md) (also known as _jail throttling_) ensures that only a fraction of the provider validator set can be jailed at any given time. + ## 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. @@ -600,7 +596,4 @@ The following command demonstrates how to run a Hermes instance in _evidence mod ```bash hermes evidence --chain ``` - -:::tip -`hermes evidence` takes a `--check-past-blocks` option giving the possibility to look for older evidence (default is 100). -::: +Note that `hermes evidence` takes a `--check-past-blocks` option giving the possibility to look for older evidence (default is 100). diff --git a/docs/docs/frequently-asked-questions.md b/docs/docs/frequently-asked-questions.md index 27623d22f6..379e0ecef2 100644 --- a/docs/docs/frequently-asked-questions.md +++ b/docs/docs/frequently-asked-questions.md @@ -1,137 +1,166 @@ --- -sidebar_position: 5 +sidebar_position: 6 title: "Frequently Asked Questions" slug: /faq --- -## What is a consumer chain? +## General -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). +### What is Interchain Security (ICS)? -Consumer chains are run on infrastructure (virtual or physical machines) distinct from the provider, have their own configurations and operating requirements. +ICS is an IBC protocol that enables a provider chain (e.g., the Cosmos Hub) to provide security to multiple [consumer chains](#what-are-consumer-chains). +This means that consumer chains will leverage the stake locked on the provider chain for block production (i.e., a cross-chain proof-of-stake system). +ICS allows anyone to launch a consumer chain using a subset, or even the entire, validator set from the provider chain. +Note that validators need to run separate infrastructure for the provider and consumer chains, resulting in different networks that only share (a subset of) the validator set. -## What happens to consumer if provider is down? +### What is the difference between ICS and Partial Set Security (PSS)? -In case the provider chain halts or experiences difficulties the consumer chain will keep operating - the provider chain and consumer chains represent different networks, which only share the validator set. +[ICS is a protocol](#what-is-interchain-security-ics). +PSS is a feature of ICS that allows a provider chain to share only a subset of its validator set with a consumer chain. +PSS differentiates between TopN and Opt-In consumer chains. +For TopN chains, the validator subset is determined by the top N% provider validators by voting power. +For Opt-In chains, the validator subset is determined by validators opting in to validate the consumer chains. +PSS allows for flexible tradeoffs between security, decentralization, and the budget a consumer chain spends on rewards to validators. -The consumer chain will not halt if the provider halts because they represent distinct networks and distinct infrastructures. Provider chain liveness does not impact consumer chain liveness. +For more details, see the [PSS feature](./features/partial-set-security.md). -However, if the `trusting_period` (currently 5 days for protocol safety reasons) elapses without receiving any updates from the provider, the consumer chain will essentially transition to a Proof of Authority chain. -This means that the validator set on the consumer will be the last validator set of the provider that the consumer knows about. +## Consumer Chains -Steps to recover from this scenario and steps to "release" the validators from their duties will be specified at a later point. -At the very least, the consumer chain could replace the validator set, remove the ICS module and perform a genesis restart. The impact of this on the IBC clients and connections is currently under careful consideration. +### What are consumer chains? -## What happens to provider if consumer is down? +Consumer chains are blockchains operated by (a subset of) the validators of the provider chain. +The ICS protocol ensures that consumer chains get information about which validators should validate on them. +This information consists of the opted in validators and their power on the consumer chains. +Note that the validators' power on the consumer chains is a function of their stake locked on the provider chain. -Consumer chains do not impact the provider chain. -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). +Consumer chains are run on infrastructure (virtual or physical machines) distinct from the provider chain, have their own configurations and operating requirements. -## Can I run the provider and consumer chains on the same machine? +Consumer chains are free to choose how they wish to operate and which modules to include. +For example, they can choose to use CosmWasm either in a permissioned or a permissionless way. +Also, consumer chains are free to perform software upgrades at any time without impacting the provider chain. -Yes, but you should favor running them in separate environments so failure of one machine does not impact your whole operation. +### How to become a consumer chain? -## Can the consumer chain have its own token? +To become a consumer chain use this [checklist](./consumer-development/onboarding.md) and check the [App integration section](./consumer-development/app-integration.md). -As any other cosmos-sdk chain the consumer chain can issue its own token, manage inflation parameters and use them to pay gas fees. +### What happens to consumers if the provider is down? -## How are Tx fees paid on consumer? +In case the provider chain halts or experiences difficulties, the consumer chains will keep operating - the provider chain and consumer chains represent different networks that only share (a subset of) the validator set. +As the validators run separate infrastructure on these networks, **_the provider chain liveness does not impact the liveness of consumer chains_**. -The consumer chain operates as any other cosmos-sdk chain. The ICS protocol does not impact the normal chain operations. +Every consumer chain communicates with the provider chain via a CCV channel -- an IBC ordered channel. +If any of the packets sent over the CCV channel timeout (see the [CCVTimeoutPeriod param](./introduction/params.md#ccvtimeoutperiod)), then the channel is closed and, consequently, the consumer chain transitions to a Proof of Authority (PoA) chain. +This means that the validator set on the consumer will no longer be updated with information from the provider. -## Are there any restrictions the consumer chains need to abide by? +### What happens to provider if any of the consumers are down? -No. Consumer chains are free to choose how they wish to operate, which modules to include, use CosmWASM in a permissioned or a permissionless way. -The only thing that separates consumer chains from standalone chains is that they share their validator set with the provider chain. +**_Consumer chains do not impact the livness of the provider chain._** +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). -## What's in it for the validators and stakers? +### Can consumer chains have their own token? -The consumer chains sends a portion of its fees and inflation as reward to the provider chain as defined by `ConsumerRedistributionFraction`. The rewards are distributed (sent to the provider) every `BlocksPerDistributionTransmission`. +As any other Cosmos SDK chains, **_consumer chains can issue their own token_** and manage inflation parameters. +Note that the ICS protocol does not impact the transaction fee system on the consumer chains. +This means consumer chains can use any token (including their own token) to pay gas fees. +For more details, see the [democracy modules](./features/democracy-modules.md#tokenomics). -:::note - `ConsumerRedistributionFraction` and `BlocksPerDistributionTransmission` are parameters defined in the `ConsumerAdditionProposal` used to create the consumer chain. These parameters can be changed via consumer chain governance. -::: +### Can consumer chains have their own governance? -## Can the consumer chain have its own governance? +Yes. ICS allows consumer chains to **_separate governance from block production_**. +Validator operators (with their stake locked on the provider) are responsible for block production, while _representatives_ (aka governators, governors) are responsible for on-chain governance. +For more details, see the [democracy modules](./features/democracy-modules.md). -**Yes.** +### Can a consumer chain modify its power shaping parameters? -In that case the validators are not necessarily part of the governance structure. Instead, their place in governance is replaced by "representatives" (governors). The representatives do not need to run validators, they simply represent the interests of a particular interest group on the consumer chain. +Yes, by issuing a [`ConsumerModificationProposal`](./features/proposals.md#consumermodificationproposal). -Validators can also be representatives but representatives are not required to run validator nodes. +### Can a Top N consumer chain become Opt-In or vice versa? -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. +Yes, by issuing a [`ConsumerModificationProposal`](./features/proposals.md#consumermodificationproposal). -## Can validators opt out of validating a consumer chain? +## Validators -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 can validators opt in to validate a consumer chain? -## How does Slashing work? +Check the [validator guide to Partial Set Security](./validators/partial-set-security-for-validators.md#how-to-opt-in-to-a-consumer-chain). -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)). +An important note is that validator the top N% of the provider chain validator set are automatically opted in on Top N consumer chains. -## Can Consumer Chains perform Software Upgrades? +### Can validators opt in to an Opt-in chain after the spawn time if nobody else opted in? -Consumer chains are standalone chains, in the sense that they can run arbitrary logic and use any modules they want (ie CosmWASM). +No, the consumer chain will be removed if nobody opted in by the spawn time. At least one validator, regardless of its voting power, must opt in before the spawn time in order for the chain can start. -Consumer chain upgrades are unlikely to impact the provider chain, as long as there are no changes to the ICS module. +### How does a validator know which consumers chains it has to validate? -## How can I connect to the testnets? +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). -Check out the [Joining Interchain Security testnet](./validators/joining-testnet.md) section. +### How many chains can a validator opt in to? -## How do I start using ICS? +There is **no** limit in the number of consumers chains a validator can choose to opt in to. -To become a consumer chain use this [checklist](./consumer-development/onboarding.md) and check the [App integration section](./consumer-development/app-integration.md) +### How can validators assign consumer keys? -## Which relayers are supported? +Check the [Key Assignment guide](./features/key-assignment.md) for specific instructions. -Currently supported versions: +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. -- Hermes 1.8.0 +Also note that validators can assign consensus keys before a consumer chain is launched (e.g., during the voting period for Top N consumer chains). -## How does key delegation work in ICS? +### What are the benefits for validators running consumer chains? -You can check the [Key Assignment Guide](./features/key-assignment.md) for specific instructions. +The consumer chains sends a portion of its block rewards (e.g., transaction fees and inflation) to the provider chain as defined by the [ConsumerRedistributionFraction param](./introduction/params.md#consumerredistributionfraction). +These rewards are sent periodically to the provider (via IBC transfers), where they are distributed **ONLY** to the _opted in_ validators and their delegators. For more details, see the [Reward Distribution feature](./features/reward-distribution.md). -## How does Partial Set Security work? +### Can validators set per consumer chain commission rates? -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. +Yes. See the [validator guide to Partial Set Security](./validators/partial-set-security-for-validators.md#how-to-set-specific-per-consumer-chain-commission-rate). -See the [Partial Set Security](./features/partial-set-security.md) section for more information. +### What are the risks for validators running consumer chains? -## How does a validator know which consumers chains it has to validate? +Validators that perform an equivocation or a light-client attack on a consumer chain are slashed on the provider chain. This is done by submitting a proof of the equivocation or the light-client attack to the provider chain. -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). +In addition, consumer chains send IBC packets via the CCV channels informing the provider when opted in validators should be jailed for downtime. +It is important to notice that _validators are not slashed for downtime on consumer chains_. +The downtime logic is custom to the consumer chain. +For example, Cosmos SDK chains can use the [slashing module](https://docs.cosmos.network/v0.50/build/modules/slashing) to configure the downtime window. -## How many chains can a validator opt in to? +For more details, see the [slashing feature](features/slashing.md). -There is **no** limit in the number of consumers chains a validator can choose to opt in to. +### Can validators run the provider and consumer chains on the same machine? -## 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. +In theory yes. +In practice, we recommend validators to run the provider and consumer chains in separate environments for fault containment, i.e., failures of one machine do not impact the entire system. -## 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 out of validating a consumer chain? -## 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. +Validators can always opt out from an Opt-In consumer chain. +Validators can only opt out from a TopN chain if they do not belong to the top N% validators. -## 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? -## 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. +Note that if all validators opt out of an Opt-In consumer chain, then the chain will halt with a consensus failure upon receiving the `VSCPacket` with an empty validator set. -## 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%. +### How to connect to the testnets? -## Can a consumer chain modify its power shaping parameters? -Yes, by issuing a [`ConsumerModificationProposal`](./features/proposals.md#consumermodificationproposal). +Check out the [Joining Interchain Security testnet](./validators/joining-testnet.md) section. + +## Integrators + +### Which relayers are supported? + +Currently supported versions: + +- Hermes `v1.8.0+` + +### How to check when the next validator update will be sent to the consumer chains? + +Validator updates are sent to consumer chains every `BlocksPerEpoch` blocks. +Depending on the status of relayers between the Hub and the consumer chains, +it might take a while for the validator updates to be processed and applied on the consumer chains. -## 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 +To query how many blocks are left until the next epoch starts, +run the following command: +```bash +interchain-security-pd query provider blocks-until-next-epoch +``` diff --git a/docs/docs/index.mdx b/docs/docs/index.mdx index 2086a4f384..576da1feee 100644 --- a/docs/docs/index.mdx +++ b/docs/docs/index.mdx @@ -6,9 +6,13 @@ import CardSection from '/src/components/Cards/CardSection'; import homeCards from '/src/components/Cards/card-data/home-cards'; -# Interchain Security Docs +# Cosmos Hub - Interchain Security -Welcome to the official Interchain Security module documentation for Cosmos-SDK based chains. +Welcome to the official documentation for the Cosmos Hub's Interchain Security - a platform for launching Cosmos-SDK chains. + +*The Cosmos Hub is the best place to launch a chain.* + +With the recent launch of [Partial Set Security](/features/partial-set-security) (PSS), it's easier than ever to leverage the Cosmos Hub validators, stake, and community for your chain. PSS provides powerful new capabilities for permissionlessly launching your chain and [shaping your validator set](/features/power-shaping). Here you can find information about Interchain Security, consumer chain development and instructions for validator onboarding. diff --git a/docs/docs/integrators/_category_.json b/docs/docs/integrators/_category_.json new file mode 100644 index 0000000000..88e78bd99d --- /dev/null +++ b/docs/docs/integrators/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Integrators Guide", + "position": 5 +} diff --git a/docs/docs/integrators/integrating_inactive_validators.md b/docs/docs/integrators/integrating_inactive_validators.md new file mode 100644 index 0000000000..dbc7408156 --- /dev/null +++ b/docs/docs/integrators/integrating_inactive_validators.md @@ -0,0 +1,27 @@ +--- +sidebar_position: 1 +--- + +# Inactive Validators Integration Guide + +With the [inactive validators feature of Interchain Security](../adrs/adr-017-allowing-inactive-validators.md), validators outside of the active set on the provider chain can validate on consumer chains that allow this. Technically, this is achieved by *increasing* the MaxValidators parameter in the staking module, to let additional validators be part of the set of bonded validators. However, to keep the set of validators participating in consensus on the Cosmos Hub the same, we introduce the MaxProviderConsensusValidators parameter in the provider module, which will restrict the number of validators that actively validate on the provider chain. + +To clarify the terminology: + +*bonded* validators are all validators that are bonded on the Cosmos Hub, and + +*active* validators are all validators that actively participate in consensus on the Cosmos Hub. + +Before the introduction of the feature, these two terms were equivalent: every bonded validator was active, and every active validator was bonded. *After* the introduction of this feature, it still holds that every active validator is bonded, but not every bonded validator is active. + +Importantly, only *active* validators receive inflation rewards from ATOM; only *active* validators may vote on behalf of their delegators in governance, and *only active* validators can get slashed for downtime (because only those validators participate in consensus and produce blocks). Apart from these differences, *bonded but inactive* validators are just like *active* validators - they can receive delegations, and they can validate on consumer chains (and receive rewards for this) just like active validators. + +The following queries will change after this upgrade: + +* `/cosmos/staking/v1beta1/pool` / `query staking pool` + +The `bonded_tokens` will include the stake of all *bonded* validators. As the number of bonded validators will be increased as part of the upgrade, the number of `bonded_tokens` is expected to have a sudden increase after the upgrade is applied. + +* All queries in the staking module that return a `Validator` + +All *bonded* validators will show with `Status=Bonded`. To identify *active* validators, query the validator set from Tendermint (https://docs.cometbft.com/v0.37/rpc/#/Info/validators or `query comet-validator-set [height]`), which will return the set of all *active* validators. \ No newline at end of file diff --git a/docs/docs/introduction/overview.md b/docs/docs/introduction/overview.md index b9f870bfab..d8938a70fa 100644 --- a/docs/docs/introduction/overview.md +++ b/docs/docs/introduction/overview.md @@ -3,37 +3,54 @@ sidebar_position: 1 --- # Overview -:::info -Interchain Security is an open sourced IBC application which allows cosmos blockchains to lease their proof-of-stake security to one another. -

-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. -::: +Interchain Security (ICS) is an open source IBC application that allows Cosmos chains to lease their proof-of-stake security to one another. +ICS allows anyone to launch a _consumer_ chain using a subset, or even the entire, validator set from the _provider_ chain 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 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. 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. +- **Tailored security.** + Consumer chains can choose the right level of security based on their needs: + Chains can choose to inherit the whole validator set from the provider, or they can launch as an opt-in chain with a subset of the provider validators. + Additionally, consumer chains have the power to shape the validator set to their specific requirements by setting allow & deny lists, capping its size, etc. + This allows for a wide range of security tradeoffs. + For example, it enables emerging projects to deploy on consumer chains that don’t need high level of security. +- **Separation of governance from block production.** + Consumer chains can separate their governance mechanism from block production. + Block production is handled by provider validators, which means it is an extension of the proof-of-state (PoS) mechanism on the provider chain. + Governance on the consumer chains can rely on the same PoS mechanism (using the same stake locked on the provider), but it doesn't have to. + For example, consumer chains can have a governance system based on proof-of-authority (PoA) or on PoS using the consumer token (which would make the consumer token a governance token). + This also allows the governance to be more decentralized without affecting consensus (i.e., increasing the number of validators usually leads to slower block production). +- **Distribution of block rewards.** + Consumer chains can choose how to distribute the block rewards (i.e., inflation and fees), what percentage to send to the provider as payment for block production, and what percentage to keep on-chain. + The rewards kept on-chain can then be distributed to the community DAO (the consumer's governance) 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. + 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. +- **Block-space sharding.** + Consumer chains are Cosmos appchains, which means that transactions on these chains do not compete with any other applications. As a result, there will be no unexpected congestion, and performance will generally be much better than on a shared smart contract platform such as Ethereum. ## Core protocol -:::info -Protocol specification is available as [ICS-028](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md) in the IBC repository. -::: - -Once an IBC connection and proper channel is established between a provider and consumer chain, the provider will continually send validator set updates to the consumer over IBC. The consumer uses these validator set updates to update its own validator set in Comet. Thus, the provider validator set is effectively replicated on the consumer. - -To ensure the security of the consumer chain, provider delegators cannot unbond their tokens until the unbonding periods of each consumer chain has passed. In practice this will not be noticeable to the provider delegators, since consumer chains will be configured to have a slightly shorter unbonding period than the provider. - -### Downtime Slashing - -If downtime is initiated by a validator on a consumer chain, a downtime packet will be relayed to the provider to jail that validator for a set amount of time. The validator who committed downtime will then miss out on staking rewards for the configured jailing period. - -### Tokenomics and Rewards - -Consumer chains are free to create their own native token which can be used for fees, and can be created on the consumer chain in the form of inflationary rewards. These rewards can be used to incentivize user behavior, for example, LPing or staking. A portion of these fees and rewards will be sent to provider chain stakers, but that proportion is completely customizable by the developers, and subject to governance. +**Validator updates**. +Once an IBC connection and channel are established between a provider and consumer chain, the provider will continually send validator set updates to the consumer over IBC. Note the provider only sends updates for opted in validators. +The consumer uses these validator set updates to update its own validator set in the consensus engine (e.g., CometBFT). + +**Slashing and jailing.** +If the opted-in validators misbehave on the consumer chains, then they will be punished on the provider chain. +ICS currently differentiates between two types of infractions -- double signing and downtime. +Double signing on consumer chains results in the misbehaving validators having their provider stake slashed and being permanently jailed on the provider, +while downtime on consumer chains results in the misbehaving validators being temporarily jailed. +Note that jailing entails removing the validator from the provider active validator set and, consequently, from any of the consumer validato sets. +This entails the validator will miss out on both staking and ICS rewards. + +**Tokenomics and rewards.** +Consumer chains are free to create their own native token which can be used for fees, and can be created on the consumer chain in the form of inflationary rewards. +These rewards can be used to incentivize user behavior, for example, LPing or staking. +A percentage of these fees and rewards will be sent to provider chain to be distributed among the opted in validators and their delegators. +The percentage is completely customizable by the developers and subject to governance. diff --git a/docs/docs/introduction/params.md b/docs/docs/introduction/params.md index 8d917b3070..3b52e772ab 100644 --- a/docs/docs/introduction/params.md +++ b/docs/docs/introduction/params.md @@ -61,21 +61,6 @@ CCVTimeoutPeriod may have different values on the provider and consumer chains. - `CCVTimeoutPeriod` on the provider **must** be larger than `ConsumerUnbondingPeriod` - `CCVTimeoutPeriod` on the consumer is initial set via the `ConsumerAdditionProposal` -### InitTimeoutPeriod -`InitTimeoutPeriod` is the maximum allowed duration for CCV channel initialization to execute. - -For any consumer chain, if the CCV channel is not established within `InitTimeoutPeriod` then the consumer chain will be removed and therefore will not be secured by the provider chain. - -The countdown starts when the `spawn_time` specified in the `ConsumerAdditionProposal` is reached. - -### VscTimeoutPeriod -`VscTimeoutPeriod` is the provider-side param that enables the provider to timeout VSC packets even when a consumer chain is not live. -If the `VscTimeoutPeriod` is ever reached for a consumer chain that chain will be considered not live and removed from interchain security. - -:::tip -`VscTimeoutPeriod` MUST be larger than the `ConsumerUnbondingPeriod`. -::: - ### BlocksPerDistributionTransmission `BlocksPerDistributionTransmission` is the number of blocks between rewards transfers from the consumer to the provider. diff --git a/docs/docs/introduction/technical-specification.md b/docs/docs/introduction/technical-specification.md deleted file mode 100644 index 581ce97137..0000000000 --- a/docs/docs/introduction/technical-specification.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -sidebar_position: 4 ---- - -# Technical Specification - -For a technical deep dive into the replicated security protocol, see the [specification](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/README.md). diff --git a/docs/docs/introduction/terminology.md b/docs/docs/introduction/terminology.md index 63668c9b43..3ad5a3696a 100644 --- a/docs/docs/introduction/terminology.md +++ b/docs/docs/introduction/terminology.md @@ -4,27 +4,33 @@ sidebar_position: 2 # Terminology -You may have heard of one or multiple buzzwords thrown around in the cosmos and wider crypto ecosystem such shared security, interchain security, replicated security, cross chain validation, and mesh security. These terms will be clarified below, before diving into any introductions. +You may have heard of one or multiple buzzwords thrown around in the cosmos and wider crypto ecosystem such shared security, interchain security, replicated security, cross chain validation, and mesh security. These terms will be clarified below. ## Shared Security -Shared security is a family of technologies that include optimistic rollups, zk-rollups, sharding and Interchain Security. Ie. any protocol or technology that can allow one blockchain to lend/share its proof-of-stake security with another blockchain or off-chain process. +Shared Security is a family of technologies that include optimistic rollups, zk-rollups, sharding and Interchain Security. +Basically, any protocol or technology that can allow one blockchain to lend/share its proof-of-stake security with another blockchain or off-chain process. -## Interchain Security +## Interchain Security (ICS) -Interchain Security is the Cosmos-specific category of Shared Security that uses IBC (Inter-Blockchain Communication), i.e. any shared security protocol built with IBC. +Interchain Security is the Cosmos-specific category of Shared Security that uses IBC (Inter-Blockchain Communication). ## 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. +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 "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 that since the introduction of [Partial Set Security](#partial-set-security-pss), a TopN consumer chain with N 100% fully replicates the security and decentralization of the provider chain. -## Partial Set Security +## Partial Set Security (PSS) -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. +A major feature of Interchain Security (also referred to as "Interchain Security V2") that 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. +PSS allows for more flexible security tradeoffs than Replicated Security. -## Mesh security +## Mesh Security -A protocol built on IBC that allows delegators on a cosmos chain to re-delegate their stake to validators in another chain's own validator set, using the original chain's token (which remains bonded on the original chain). For a deeper exploration of mesh security, see [Replicated vs. Mesh Security on the Informal Blog](https://informal.systems/blog/replicated-vs-mesh-security). +A protocol built on IBC that allows delegators on a Cosmos chain to re-delegate their stake to validators in another chain's own validator set, using the original chain's token (which remains bonded on the original chain). For a deeper exploration of Mesh Security, see [Replicated vs. Mesh Security on the Informal Blog](https://informal.systems/blog/replicated-vs-mesh-security). ## Consumer Chain @@ -35,8 +41,8 @@ Interchain Security allows a subset of the provider chain's validator set to val Chain that is secured by its own validator set. This chain does not participate in Interchain Security. -Standalone chains may sometimes be called "sovereign" - the terms are synonymous. - ## Changeover Procedure -Chains that were not initially launched as consumers of Interchain Security can still participate in the protocol and leverage the economic security of the provider chain. The process where a standalone chain transitions to being a replicated consumer chain is called the **changeover procedure** and is part of the interchain security protocol. After the changeover, the new consumer chain will retain all existing state, including the IBC clients, connections and channels already established by the chain. +Chains that were not initially launched as consumers of Interchain Security can still participate in the protocol and leverage the economic security of the provider chain. +The process where a standalone chain transitions to being a replicated consumer chain is called the **changeover procedure** and is part of the ICS protocol. +After the changeover, the new consumer chain will retain all existing state, including the IBC clients, connections and channels already established by the chain. diff --git a/docs/docs/validators/partial-set-security-for-validators.md b/docs/docs/validators/partial-set-security-for-validators.md index 2d6b2dd4a2..a87cf56482 100644 --- a/docs/docs/validators/partial-set-security-for-validators.md +++ b/docs/docs/validators/partial-set-security-for-validators.md @@ -4,11 +4,12 @@ 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. +The [Partial Set Security (PSS) feature](../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 @@ -39,19 +40,20 @@ and this command to see the currently proposed consumer chains: 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. +:::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. ::: +Note that 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 @@ -60,26 +62,21 @@ 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. -::: +The opting out mechanism has the following rules: -:::warning -If a validator moves from the Top N to outside of the top N% of the validators on the provider, it will **not** +- A validator cannot opt out from a Top N chain if it belongs to the top N% validators of the provider. +- 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` +- A validator should 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. -::: +not have to validate the consumer chain any longer. Otherwise, the validator risks getting jailed for downtime. :::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. +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. This can be done with the following command: ```bash @@ -91,25 +88,20 @@ 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`). - 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 -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. + +PSS introduces a number of queries to assist validators in determining 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. @@ -120,13 +112,13 @@ interchain-security-pd query provider has-to-validate @@ -151,6 +141,7 @@ interchain-security-pd query provider consumer-opted-in-validators @@ -160,14 +151,13 @@ ones that are currently (or in the future, see warning) validating the consumer 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 +Note that 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 interchain-security-pd query provider validator-consumer-commission-rate diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 17ff9aeb7b..1f3b46069e 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -39,21 +39,7 @@ const config = { docs: { sidebarPath: require.resolve("./sidebars.js"), routeBasePath: "/", - versions: { - current: { - path: "/", - label: "main", - banner: "unreleased", - }, - "v4.2.0-docs": { - path: "/v4.2.0/", - label: "v4.2.0", - banner: "none", - }, - "v5.0.0": { - banner: "unreleased", - }, - }, + lastVersion: "current", remarkPlugins: [remarkMath], rehypePlugins: [rehypeKatex], exclude: ["**/templates/*"], diff --git a/docs/src/components/Cards/card-data/home-cards.js b/docs/src/components/Cards/card-data/home-cards.js index fa2a7f4b69..8a90012c1c 100644 --- a/docs/src/components/Cards/card-data/home-cards.js +++ b/docs/src/components/Cards/card-data/home-cards.js @@ -1,23 +1,18 @@ const homeCards = [ { href: '/interchain-security/introduction/overview', - header: 'Basic concepts', + header: 'Basic Concepts', summary: 'Get started with the basic concepts and ideas.' }, { - href: '/interchain-security/consumer-development/app-integration', - header: 'Start building', - summary: 'Click here to start building with Interchain security' - }, - { - href: '/interchain-security/features/key-assignment', - header: 'Feature: Key Assignment', - summary: 'Learn about the key assignment feature', + href: '/interchain-security/faq', + header: 'FAQ', + summary: 'Frequently asked questions about the protocol and its implications' }, { - href: '/interchain-security/features/reward-distribution', - header: 'Feature: Reward Distribution', - summary: 'Learn about consumer chain rewards distribution', + href: '/interchain-security/consumer-development/app-integration', + header: 'Start Building', + summary: 'Click here to start building with Interchain Security' }, { href: '/interchain-security/consumer-development/onboarding', @@ -25,9 +20,14 @@ const homeCards = [ summary: 'Checklist to help you integrate Interchain Security, get support and onboard validators' }, { - href: '/interchain-security/faq', - header: 'FAQ', - summary: 'Frequently asked questions about the protocol and its implications' + href: 'interchain-security/validators/overview', + header: 'Become an ICS Validator', + summary: 'Start validating on consumer chains' + }, + { + href: '/interchain-security/features/key-assignment', + header: 'Assign Consumer Keys', + summary: 'Learn how to assign separate key on the consumer chains', }, ] diff --git a/docs/supported_versions.json b/docs/supported_versions.json new file mode 100644 index 0000000000..2609dbc8a1 --- /dev/null +++ b/docs/supported_versions.json @@ -0,0 +1,4 @@ +[ + "v5.1.0", + "v4.4.1" +] \ No newline at end of file diff --git a/docs/sync_versions.sh b/docs/sync_versions.sh index 977a69700c..f8fcb0cd23 100755 --- a/docs/sync_versions.sh +++ b/docs/sync_versions.sh @@ -7,7 +7,7 @@ if git diff-index --quiet HEAD --; then # initial branch COMMIT=$(git rev-parse HEAD) - for version in $(jq -r .[] versions.json); do + for version in $(jq -r .[] supported_versions.json); do echo "Building docusaurus $version docs ..." git checkout $version npm cache clean --force && npm install && npm run docusaurus docs:version $version diff --git a/docs/versions.json b/docs/versions.json deleted file mode 100644 index bba35efde5..0000000000 --- a/docs/versions.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - "v4.2.0-docs", - "v5.0.0" -] \ No newline at end of file diff --git a/go.mod b/go.mod index 5a7eaa9368..4baefd82e8 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/cometbft/cometbft v0.38.9 github.com/cometbft/cometbft-db v0.12.0 // indirect github.com/cosmos/cosmos-sdk v0.50.8 - github.com/cosmos/gogoproto v1.5.0 + github.com/cosmos/gogoproto v1.7.0 github.com/cosmos/ics23/go v0.10.0 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.4 @@ -16,14 +16,14 @@ require ( github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/kylelemons/godebug v1.1.0 github.com/oxyno-zeta/gomock-extra-matcher v1.2.0 - github.com/spf13/cast v1.6.0 + github.com/spf13/cast v1.7.0 github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.9.0 - github.com/tidwall/gjson v1.17.1 + github.com/tidwall/gjson v1.17.3 golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.20.0 // indirect + golang.org/x/sys v0.22.0 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.1 @@ -147,19 +147,19 @@ require ( require ( cosmossdk.io/client/v2 v2.0.0-beta.1 cosmossdk.io/collections v0.4.0 - cosmossdk.io/log v1.3.1 + cosmossdk.io/log v1.4.1 cosmossdk.io/store v1.1.0 cosmossdk.io/tools/confix v0.1.1 cosmossdk.io/x/evidence v0.1.0 cosmossdk.io/x/feegrant v0.1.0 - cosmossdk.io/x/tx v0.13.3 + cosmossdk.io/x/tx v0.13.4 cosmossdk.io/x/upgrade v0.1.1 github.com/cosmos/cosmos-db v1.0.2 github.com/cosmos/ibc-go/modules/capability v1.0.0 - github.com/cosmos/ibc-go/v8 v8.3.2 + github.com/cosmos/ibc-go/v8 v8.4.0 github.com/informalsystems/itf-go v0.0.1 github.com/spf13/viper v1.18.2 - golang.org/x/mod v0.17.0 + golang.org/x/mod v0.20.0 google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 ) @@ -195,7 +195,7 @@ require ( github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect github.com/oklog/run v1.1.0 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect - github.com/rs/zerolog v1.32.0 // indirect + github.com/rs/zerolog v1.33.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 diff --git a/go.sum b/go.sum index a8880a7291..c1fa7b772a 100644 --- a/go.sum +++ b/go.sum @@ -194,8 +194,8 @@ cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98ok cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= -cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI= -cosmossdk.io/log v1.3.1/go.mod h1:2/dIomt8mKdk6vl3OWJcPk2be3pGOS8OQaLUM/3/tCM= +cosmossdk.io/log v1.4.1 h1:wKdjfDRbDyZRuWa8M+9nuvpVYxrEOwbD/CA8hvhU8QM= +cosmossdk.io/log v1.4.1/go.mod h1:k08v0Pyq+gCP6phvdI6RCGhLf/r425UT6Rk/m+o74rU= cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= cosmossdk.io/store v1.1.0 h1:LnKwgYMc9BInn9PhpTFEQVbL9UK475G2H911CGGnWHk= @@ -208,8 +208,8 @@ cosmossdk.io/x/evidence v0.1.0 h1:J6OEyDl1rbykksdGynzPKG5R/zm6TacwW2fbLTW4nCk= cosmossdk.io/x/evidence v0.1.0/go.mod h1:hTaiiXsoiJ3InMz1uptgF0BnGqROllAN8mwisOMMsfw= cosmossdk.io/x/feegrant v0.1.0 h1:c7s3oAq/8/UO0EiN1H5BIjwVntujVTkYs35YPvvrdQk= cosmossdk.io/x/feegrant v0.1.0/go.mod h1:4r+FsViJRpcZif/yhTn+E0E6OFfg4n0Lx+6cCtnZElU= -cosmossdk.io/x/tx v0.13.3 h1:Ha4mNaHmxBc6RMun9aKuqul8yHiL78EKJQ8g23Zf73g= -cosmossdk.io/x/tx v0.13.3/go.mod h1:I8xaHv0rhUdIvIdptKIqzYy27+n2+zBVaxO6fscFhys= +cosmossdk.io/x/tx v0.13.4 h1:Eg0PbJgeO0gM8p5wx6xa0fKR7hIV6+8lC56UrsvSo0Y= +cosmossdk.io/x/tx v0.13.4/go.mod h1:BkFqrnGGgW50Y6cwTy+JvgAhiffbGEKW6KF9ufcDpvk= cosmossdk.io/x/upgrade v0.1.1 h1:aoPe2gNvH+Gwt/Pgq3dOxxQVU3j5P6Xf+DaUJTDZATc= cosmossdk.io/x/upgrade v0.1.1/go.mod h1:MNLptLPcIFK9CWt7Ra//8WUZAxweyRDNcbs5nkOcQy0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -351,14 +351,14 @@ github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4x github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= -github.com/cosmos/gogoproto v1.5.0 h1:SDVwzEqZDDBoslaeZg+dGE55hdzHfgUA40pEanMh52o= -github.com/cosmos/gogoproto v1.5.0/go.mod h1:iUM31aofn3ymidYG6bUR5ZFrk+Om8p5s754eMUcyp8I= +github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fro= +github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0= github.com/cosmos/iavl v1.1.2 h1:zL9FK7C4L/P4IF1Dm5fIwz0WXCnn7Bp1M2FxH0ayM7Y= github.com/cosmos/iavl v1.1.2/go.mod h1:jLeUvm6bGT1YutCaL2fIar/8vGUE8cPZvh/gXEWDaDM= github.com/cosmos/ibc-go/modules/capability v1.0.0 h1:r/l++byFtn7jHYa09zlAdSeevo8ci1mVZNO9+V0xsLE= github.com/cosmos/ibc-go/modules/capability v1.0.0/go.mod h1:D81ZxzjZAe0ZO5ambnvn1qedsFQ8lOwtqicG6liLBco= -github.com/cosmos/ibc-go/v8 v8.3.2 h1:8X1oHHKt2Bh9hcExWS89rntLaCKZp2EjFTUSxKlPhGI= -github.com/cosmos/ibc-go/v8 v8.3.2/go.mod h1:WVVIsG39jGrF9Cjggjci6LzySyWGloz194sjTxiGNIE= +github.com/cosmos/ibc-go/v8 v8.4.0 h1:K2PfX0AZ+1XKZytHGEMuSjQXG/MZshPb83RSTQt2+cE= +github.com/cosmos/ibc-go/v8 v8.4.0/go.mod h1:zh6x1osR0hNvEcFrC/lhGD08sMfQmr9wHVvZ/mRWMCs= github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= @@ -929,8 +929,8 @@ github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo= github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= -github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -958,8 +958,8 @@ github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIK github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= @@ -998,8 +998,8 @@ github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2l github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= -github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= -github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94= +github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= @@ -1124,8 +1124,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.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.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.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= @@ -1327,8 +1327,8 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/proto/interchain_security/ccv/provider/v1/genesis.proto b/proto/interchain_security/ccv/provider/v1/genesis.proto index 269743721e..34c0daf633 100644 --- a/proto/interchain_security/ccv/provider/v1/genesis.proto +++ b/proto/interchain_security/ccv/provider/v1/genesis.proto @@ -11,6 +11,21 @@ import "interchain_security/ccv/provider/v1/provider.proto"; // GenesisState defines the CCV provider chain genesis state message GenesisState { + // Reserve 3rd slot for removed unbonding_ops field + reserved 3; + + // Reserve 4th slot for removed mature_unbonding_ops field + reserved 4; + + // Reserve 11th slot for consumer_addrs_to_prune field + reserved 11; + + // Reserve 12th slot for removed init_timeout_timestamps field + reserved 12; + + // Reserve 13th slot for removed exported_vsc_send_timestamps field + reserved 13; + // strictly positive and set to 1 (DefaultValsetUpdateID) for a new chain uint64 valset_update_id = 1; // empty for a new chain @@ -19,11 +34,6 @@ message GenesisState { (gogoproto.moretags) = "yaml:\"consumer_states\"" ]; // empty for a new chain - repeated interchain_security.ccv.provider.v1.UnbondingOp unbonding_ops = 3 - [ (gogoproto.nullable) = false ]; - // empty for a new chain - interchain_security.ccv.provider.v1.MaturedUnbondingOps mature_unbonding_ops = 4; - // empty for a new chain repeated ValsetUpdateIdToHeight valset_update_id_to_height = 5 [ (gogoproto.nullable) = false ]; // empty for a new chain @@ -39,14 +49,9 @@ message GenesisState { // empty for a new chain repeated ValidatorByConsumerAddr validators_by_consumer_addr = 10 [ (gogoproto.nullable) = false ]; - // empty for a new chain - repeated ConsumerAddrsToPrune consumer_addrs_to_prune = 11 - [ (gogoproto.nullable) = false ]; - repeated interchain_security.ccv.provider.v1.InitTimeoutTimestamp init_timeout_timestamps = 12 - [ (gogoproto.nullable) = false ]; - - repeated interchain_security.ccv.provider.v1.ExportedVscSendTimestamp exported_vsc_send_timestamps = 13 + // empty for a new chain + repeated ConsumerAddrsToPruneV2 consumer_addrs_to_prune_v2 = 14 [ (gogoproto.nullable) = false ]; } @@ -54,6 +59,9 @@ message GenesisState { // // Note this type is only used internally to the provider CCV module. message ConsumerState { + // Reserve 8th slot for removed unbonding_ops_index field + reserved 8; + // ChainID defines the chain ID for the consumer chain string chain_id = 1; // ChannelID defines the IBC channel ID for the consumer chain @@ -70,10 +78,6 @@ message ConsumerState { repeated interchain_security.ccv.v1.ValidatorSetChangePacketData pending_valset_changes = 6 [ (gogoproto.nullable) = false ]; repeated string slash_downtime_ack = 7; - // UnbondingOpsIndex defines the unbonding operations waiting on this consumer - // chain - repeated interchain_security.ccv.provider.v1.VscUnbondingOps - unbonding_ops_index = 8 [ (gogoproto.nullable) = false ]; } // ValsetUpdateIdToHeight defines the genesis information for the mapping diff --git a/proto/interchain_security/ccv/provider/v1/provider.proto b/proto/interchain_security/ccv/provider/v1/provider.proto index 26648c3fc5..7e1836cf38 100644 --- a/proto/interchain_security/ccv/provider/v1/provider.proto +++ b/proto/interchain_security/ccv/provider/v1/provider.proto @@ -106,6 +106,10 @@ message ConsumerAdditionProposal { repeated string allowlist = 18; // Corresponds to a list of provider consensus addresses of validators that CANNOT validate the consumer chain. repeated string denylist = 19; + // Corresponds to the minimal amount of (provider chain) stake required to validate on the consumer chain. + uint64 min_stake = 20; + // Corresponds to whether inactive validators are allowed to validate the consumer chain. + bool allow_inactive_vals = 21; } // ConsumerRemovalProposal is a governance proposal on the provider chain to @@ -156,6 +160,10 @@ message ConsumerModificationProposal { repeated string allowlist = 7; // Corresponds to a list of provider consensus addresses of validators that CANNOT validate the consumer chain. repeated string denylist = 8; + // Corresponds to the minimal amount of (provider chain) stake required to validate on the consumer chain. + uint64 min_stake = 9; + // Corresponds to whether inactive validators are allowed to validate the consumer chain. + bool allow_inactive_vals = 10; } @@ -215,6 +223,12 @@ message GlobalSlashEntry { // Params defines the parameters for CCV Provider module message Params { + // Reserve 4th slot for removed init_timeout_period param + reserved 4; + + // Reserve 5th slot for removed vsc_timeout_period param + reserved 5; + // Reserve 8th slot for removed max throttled packet param reserved 8; @@ -225,16 +239,6 @@ message Params { // Sent IBC packets will timeout after this duration google.protobuf.Duration ccv_timeout_period = 3 [ (gogoproto.nullable) = false, (gogoproto.stdduration) = true ]; - // The channel initialization (IBC channel opening handshake) will timeout - // after this duration - google.protobuf.Duration init_timeout_period = 4 - [ (gogoproto.nullable) = false, (gogoproto.stdduration) = true ]; - // The VSC packets sent by the provider will timeout after this duration. - // Note that unlike ccv_timeout_period which is an IBC param, - // the vsc_timeout_period is a provider-side param that enables the provider - // to timeout VSC packets even when a consumer chain is not live. - google.protobuf.Duration vsc_timeout_period = 5 - [ (gogoproto.nullable) = false, (gogoproto.stdduration) = true ]; // The period for which the slash meter is replenished google.protobuf.Duration slash_meter_replenish_period = 6 @@ -254,6 +258,10 @@ message Params { // 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; + + // The maximal number of validators that will be passed + // to the consensus engine on the provider. + int64 max_provider_consensus_validators = 12; } // SlashAcks contains cons addresses of consumer chain validators @@ -283,49 +291,12 @@ message ChannelToChain { string chain_id = 2; } -// VscUnbondingOps contains the IDs of unbonding operations that are waiting for -// at least one VSCMaturedPacket with vscID from a consumer chain -message VscUnbondingOps { - uint64 vsc_id = 1; - repeated uint64 unbonding_op_ids = 2; -} - -// UnbondingOp contains the ids of consumer chains that need to unbond before -// the unbonding operation with the given ID can unbond -message UnbondingOp { - uint64 id = 1; - // consumer chains that are still unbonding - repeated string unbonding_consumer_chains = 2; -} - -message InitTimeoutTimestamp { - string chain_id = 1; - uint64 timestamp = 2; -} - -message VscSendTimestamp { - uint64 vsc_id = 1; - google.protobuf.Timestamp timestamp = 2 - [ (gogoproto.stdtime) = true, (gogoproto.nullable) = false ]; -} - // ValidatorSetChangePackets is a pb list of ccv.ValidatorSetChangePacketData. message ValidatorSetChangePackets { repeated interchain_security.ccv.v1.ValidatorSetChangePacketData list = 1 [ (gogoproto.nullable) = false ]; } -// MaturedUnbondingOps defines a list of ids corresponding to ids of matured -// unbonding operations. -message MaturedUnbondingOps { repeated uint64 ids = 1; } - -// ExportedVscSendTimestamps is VscSendTimestamp with chainID info for exporting to genesis -message ExportedVscSendTimestamp { - string chain_id = 1; - repeated VscSendTimestamp vsc_send_timestamps = 2 - [ (gogoproto.nullable) = false ]; -} - // // Key assignment section // @@ -354,23 +325,27 @@ message ValidatorByConsumerAddr { bytes provider_addr = 3; } -// Used to serialize the ConsumerAddrsToPrune index from key assignment -// ConsumerAddrsToPrune: (chainID, vscID uint64) -> consumerAddrs AddressList -message ConsumerAddrsToPrune { +// Used to serialize the ConsumerAddrsToPruneV2 index from key assignment +// ConsumerAddrsToPruneV2: (chainID, pruneTs time.Time) -> consumerAddrs AddressList +message ConsumerAddrsToPruneV2 { string chain_id = 1; - uint64 vsc_id = 2; + google.protobuf.Timestamp prune_ts = 2 + [ (gogoproto.stdtime) = true, (gogoproto.nullable) = false ]; AddressList consumer_addrs = 3; } -// ConsumerValidator is used to facilitate epoch-based transitions. It contains relevant info for -// a validator that is expected to validate on a consumer chain during an epoch. -message ConsumerValidator { +// ConsensusValidator is used to express a validator that +// should be validating on a chain. +// It contains relevant info for +// a validator that is expected to validate on +// either the provider or a consumer chain. +message ConsensusValidator { // validator's consensus address on the provider chain bytes provider_cons_addr = 1; // voting power the validator has during this epoch int64 power = 2; // public key the validator uses on the consumer chain during this epoch - tendermint.crypto.PublicKey consumer_public_key = 3; + tendermint.crypto.PublicKey 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 diff --git a/proto/interchain_security/ccv/provider/v1/query.proto b/proto/interchain_security/ccv/provider/v1/query.proto index 9ea3aa6bec..0ac1baa477 100644 --- a/proto/interchain_security/ccv/provider/v1/query.proto +++ b/proto/interchain_security/ccv/provider/v1/query.proto @@ -128,13 +128,6 @@ service Query { "/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) { - 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. @@ -143,6 +136,14 @@ service Query { option (google.api.http).get = "/interchain_security/ccv/provider/consumer_validators/{chain_id}"; } + + // QueryBlocksUntilNextEpoch returns the number of blocks until the next epoch + // starts and validator updates are sent to the consumer chains + rpc QueryBlocksUntilNextEpoch(QueryBlocksUntilNextEpochRequest) + returns (QueryBlocksUntilNextEpochResponse) { + option (google.api.http).get = + "/interchain_security/ccv/provider/blocks_until_next_epoch"; + } } message QueryConsumerGenesisRequest { string chain_id = 1; } @@ -186,6 +187,11 @@ message 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; + // Corresponds to the minimal amount of (provider chain) stake required to validate on the consumer chain. + uint64 min_stake = 9; + // Corresponds to whether inactive validators are allowed to validate the consumer chain. + bool allow_inactive_vals = 10; + } message QueryValidatorConsumerAddrRequest { @@ -291,6 +297,11 @@ message QueryConsumerValidatorsValidator { tendermint.crypto.PublicKey consumer_key = 2; // The power of the validator used on the consumer chain int64 power = 3; + // The rate to charge delegators on the consumer chain, as a fraction + string rate = 4 [ + (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", + (gogoproto.nullable) = false + ]; } message QueryConsumerValidatorsResponse { @@ -320,9 +331,9 @@ message QueryValidatorConsumerCommissionRateResponse { ]; } -message QueryOldestUnconfirmedVscRequest { string chain_id = 1; } +message QueryBlocksUntilNextEpochRequest { } -message QueryOldestUnconfirmedVscResponse { - interchain_security.ccv.provider.v1.VscSendTimestamp vsc_send_timestamp = 1 - [ (gogoproto.nullable) = false ]; +message QueryBlocksUntilNextEpochResponse { + // The number of blocks until the next epoch starts + uint64 blocks_until_next_epoch = 1; } diff --git a/proto/interchain_security/ccv/provider/v1/tx.proto b/proto/interchain_security/ccv/provider/v1/tx.proto index 91d7e517a3..113923b11c 100644 --- a/proto/interchain_security/ccv/provider/v1/tx.proto +++ b/proto/interchain_security/ccv/provider/v1/tx.proto @@ -30,6 +30,7 @@ service Msg { rpc OptOut(MsgOptOut) returns (MsgOptOutResponse); rpc SetConsumerCommissionRate(MsgSetConsumerCommissionRate) returns (MsgSetConsumerCommissionRateResponse); rpc ConsumerModification(MsgConsumerModification) returns (MsgConsumerModificationResponse); + rpc ChangeRewardDenoms(MsgChangeRewardDenoms) returns (MsgChangeRewardDenomsResponse); } @@ -183,6 +184,10 @@ message MsgConsumerAddition { repeated string denylist = 17; // signer address string authority = 18 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // Corresponds to the minimal amount of (provider chain) stake required to validate on the consumer chain. + uint64 min_stake = 19; + // Corresponds to whether inactive validators are allowed to validate the consumer chain. + bool allow_inactive_vals = 20; } // MsgConsumerAdditionResponse defines response type for MsgConsumerAddition messages @@ -270,6 +275,8 @@ message MsgOptOutResponse {} message MsgSetConsumerCommissionRate { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; + option (cosmos.msg.v1.signer) = "signer"; + // 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 @@ -281,7 +288,9 @@ message MsgSetConsumerCommissionRate { (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false ]; - } + // signer address + string signer = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} message MsgSetConsumerCommissionRateResponse {} @@ -292,6 +301,8 @@ message MsgSetConsumerCommissionRateResponse {} // // Note: this replaces ConsumerModificationProposal which is deprecated and will be removed soon message MsgConsumerModification { + option (cosmos.msg.v1.signer) = "authority"; + // the title of the proposal string title = 1; // the description of the proposal @@ -319,6 +330,10 @@ message MsgConsumerModification { repeated string denylist = 8; // signer address string authority = 9 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // Corresponds to the minimal amount of (provider chain) stake required to validate on the consumer chain. + uint64 min_stake = 10; + // Corresponds to whether inactive validators are allowed to validate the consumer chain. + bool allow_inactive_vals = 11; } message MsgConsumerModificationResponse {} diff --git a/tests/e2e/README.md b/tests/e2e/README.md index cc74e57834..eb8e660396 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -187,6 +187,63 @@ which is done in `getChainState`. Typically, this is ultimately done, like actions, by issuing commands to the chain binary inside the docker container. See how this is done e.g. for `getBalance`. +## Defining extra validators + +In `config.go`, you can define extra validators that are not part of the default test configuration, for example +for tests that need more than 3 validators. + +To create a configuration for a new validator, it suffices to add a new `ValidatorConfig`. For example, this is the validator config for alice: + +``` +ValidatorID("alice"): { + Mnemonic: "pave immune ethics wrap gain ceiling always holiday employ earth tumble real ice engage false unable carbon equal fresh sick tattoo nature pupil nuclear", + DelAddress: "cosmos19pe9pg5dv9k5fzgzmsrgnw9rl9asf7ddwhu7lm", + DelAddressOnConsumer: "consumer19pe9pg5dv9k5fzgzmsrgnw9rl9asf7ddtz33vu", + ValoperAddress: "cosmosvaloper19pe9pg5dv9k5fzgzmsrgnw9rl9asf7ddtrgtng", + ValoperAddressOnConsumer: "consumervaloper19pe9pg5dv9k5fzgzmsrgnw9rl9asf7ddy6jwzg", + ValconsAddress: "cosmosvalcons1qmq08eruchr5sf5s3rwz7djpr5a25f7xw4mceq", + ValconsAddressOnConsumer: "consumervalcons1qmq08eruchr5sf5s3rwz7djpr5a25f7xpvpagq", + PrivValidatorKey: `{"address":"06C0F3E47CC5C748269088DC2F36411D3AAA27C6","pub_key":{"type":"tendermint/PubKeyEd25519","value":"RrclQz9bIhkIy/gfL485g3PYMeiIku4qeo495787X10="},"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"uX+ZpDMg89a6gtqs/+MQpCTSqlkZ0nJQJOhLlCJvwvdGtyVDP1siGQjL+B8vjzmDc9gx6IiS7ip6jj3nvztfXQ=="}}`, + NodeKey: `{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"fjw4/DAhyRPnwKgXns5SV7QfswRSXMWJpHS7TyULDmJ8ofUc5poQP8dgr8bZRbCV5RV8cPqDq3FPdqwpmUbmdA=="}}`, + IpSuffix: "4", + + // consumer chain assigned key + ConsumerMnemonic: "exile install vapor thing little toss immune notable lounge december final easy strike title end program interest quote cloth forget forward job october twenty", + ConsumerDelAddress: "consumer1eeeggku6dzk3mv7wph3zq035rhtd890sh9rl32", + ConsumerDelAddressOnProvider: "cosmos1eeeggku6dzk3mv7wph3zq035rhtd890sjswszd", + ConsumerValoperAddress: "consumervaloper1eeeggku6dzk3mv7wph3zq035rhtd890scaqql7", + ConsumerValoperAddressOnProvider: "cosmosvaloper1eeeggku6dzk3mv7wph3zq035rhtd890shy69w7", + ConsumerValconsAddress: "consumervalcons1muys5jyqk4xd27e208nym85kn0t4zjcfk9q5ce", + ConsumerValconsAddressOnProvider: "cosmosvalcons1muys5jyqk4xd27e208nym85kn0t4zjcfeu63fe", + ConsumerValPubKey: `{"@type":"/cosmos.crypto.ed25519.PubKey","key":"ujY14AgopV907IYgPAk/5x8c9267S4fQf89nyeCPTes="}`, + ConsumerPrivValidatorKey: `{"address":"DF090A4880B54CD57B2A79E64D9E969BD7514B09","pub_key":{"type":"tendermint/PubKeyEd25519","value":"ujY14AgopV907IYgPAk/5x8c9267S4fQf89nyeCPTes="},"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"TRJgf7lkTjs/sj43pyweEOanyV7H7fhnVivOi0A4yjW6NjXgCCilX3TshiA8CT/nHxz3brtLh9B/z2fJ4I9N6w=="}}`, + ConsumerNodeKey: `{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"F966RL9pi20aXRzEBe4D0xRQJtZt696Xxz44XUON52cFc83FMn1WXJbP6arvA2JPyn2LA3DLKCFHSgALrCGXGA=="}}`, + UseConsumerKey: false, + }, +``` + +Here is a short guide for generating this config for a new validator: +``` +# Commands +interchain-security-pd init bob --home ./bob +cat ./bob/config/priv_validator_key.json # gets you the private key +interchain-security-pd tendermint show-address --home ./bob # returns valcons +interchain-security-pd keys add bob --keyring-backend test --home ./bob --output json # gives mnemonic, account address +interchain-security-pd keys show bob --keyring-backend test --bech=val --home ./bob --output json # returns valoper address +cat ./bob/config/node_key.json # returns the node key +``` + +Fill these values into the `ValidatorConfig` struct in `config.go`. +To get the values for the consumer chain, you can simply run the command in question using the +`interchain-security-cd` binary. +For example, +``` +interchain-security-cd tendermint show-address --home ./bob +``` +will return the validator consensus address on the consumer chain. + +One important note is that the `IPSuffix` field should be unique for each validator. + ## Traces It is possible to dump the test cases (in the form of actions+state checks) diff --git a/tests/e2e/action_rapid_test.go b/tests/e2e/action_rapid_test.go index b720d3557c..004eb30d95 100644 --- a/tests/e2e/action_rapid_test.go +++ b/tests/e2e/action_rapid_test.go @@ -99,6 +99,7 @@ func GetActionGen() *rapid.Generator[any] { func CreateSubmitChangeRewardDenomsProposalActionGen() *rapid.Generator[SubmitChangeRewardDenomsProposalAction] { return rapid.Custom(func(t *rapid.T) SubmitChangeRewardDenomsProposalAction { return SubmitChangeRewardDenomsProposalAction{ + Chain: GetChainIDGen().Draw(t, "Chain"), From: GetValidatorIDGen().Draw(t, "From"), Deposit: rapid.Uint().Draw(t, "Deposit"), Denom: rapid.String().Draw(t, "Denom"), @@ -163,9 +164,9 @@ func GetStartSovereignChainActionGen() *rapid.Generator[StartSovereignChainActio }) } -func GetSubmitLegacyUpgradeProposalActionGen() *rapid.Generator[LegacyUpgradeProposalAction] { - return rapid.Custom(func(t *rapid.T) LegacyUpgradeProposalAction { - return LegacyUpgradeProposalAction{ +func GetSubmitLegacyUpgradeProposalActionGen() *rapid.Generator[UpgradeProposalAction] { + return rapid.Custom(func(t *rapid.T) UpgradeProposalAction { + return UpgradeProposalAction{ ChainID: GetChainIDGen().Draw(t, "ChainID"), UpgradeTitle: rapid.String().Draw(t, "UpgradeTitle"), Proposer: GetValidatorIDGen().Draw(t, "Proposer"), diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index b7207d72c8..088ad19801 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "encoding/base64" "encoding/json" "fmt" "log" @@ -263,11 +264,117 @@ type SubmitConsumerAdditionProposalAction struct { ValidatorSetCap uint32 Allowlist []string Denylist []string + MinStake uint64 + AllowInactiveVals bool } func (tr Chain) submitConsumerAdditionProposal( action SubmitConsumerAdditionProposalAction, verbose bool, +) { + spawnTime := tr.testConfig.containerConfig.Now.Add(time.Duration(action.SpawnTime) * time.Millisecond) + params := ccvtypes.DefaultParams() + template := ` + { + "messages": [ + { + "@type": "/interchain_security.ccv.provider.v1.MsgConsumerAddition", + "chain_id": "%s", + "initial_height": { + "revision_number": "%d", + "revision_height": "%d" + }, + "genesis_hash": "%s", + "binary_hash": "%s", + "spawn_time": "%s", + "unbonding_period": "%s", + "ccv_timeout_period": "%s", + "transfer_timeout_period": "%s", + "consumer_redistribution_fraction": "%s", + "blocks_per_distribution_transmission": "%d", + "historical_entries": "%d", + "distribution_transmission_channel": "%s", + "top_N": %d, + "validators_power_cap": %d, + "validator_set_cap": %d, + "allowlist": %s, + "denylist": %s, + "authority": "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn", + "allow_inactive_vals": %t, + "min_stake": "%d" + } + ], +"metadata": "ipfs://CID", +"deposit": "%dstake", +"title": "Propose the addition of a new chain", +"summary": "Gonna be a great chain", +"expedited": false +}` + jsonStr := fmt.Sprintf(template, + string(tr.testConfig.chainConfigs[action.ConsumerChain].ChainId), + action.InitialHeight.RevisionNumber, + action.InitialHeight.RevisionHeight, + base64.StdEncoding.EncodeToString([]byte("gen_hash")), + base64.StdEncoding.EncodeToString([]byte("bin_hash")), + spawnTime.Local().Format(time.RFC3339Nano), + params.UnbondingPeriod, + params.CcvTimeoutPeriod, + params.TransferTimeoutPeriod, + params.ConsumerRedistributionFraction, + params.BlocksPerDistributionTransmission, + params.HistoricalEntries, + action.DistributionChannel, + action.TopN, + action.ValidatorsPowerCap, + action.ValidatorSetCap, + action.Allowlist, + action.Denylist, + action.AllowInactiveVals, + action.MinStake, + action.Deposit) + + //#nosec G204 -- bypass unsafe quoting warning (no production code) + proposalFile := "/consumer-addition.proposal" + bz, err := tr.target.ExecCommand( + "/bin/bash", "-c", fmt.Sprintf(`echo '%s' > %s`, jsonStr, proposalFile), + ).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + // CONSUMER ADDITION PROPOSAL + cmd := tr.target.ExecCommand( + tr.testConfig.chainConfigs[action.Chain].BinaryName, + "tx", "gov", "submit-proposal", proposalFile, + `--from`, `validator`+fmt.Sprint(action.From), + `--chain-id`, string(tr.testConfig.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 { + fmt.Println("submitConsumerAdditionProposal cmd:", cmd.String()) + fmt.Println("submitConsumerAdditionProposal json:", jsonStr) + } + bz, err = cmd.CombinedOutput() + if err != nil { + log.Fatal("submit-proposal failed:", err, "\n", string(bz)) + } + + if verbose { + fmt.Println("submitConsumerAdditionProposal output:", string(bz)) + } + + // wait for inclusion in a block -> '--broadcast-mode block' is deprecated + tr.waitBlocks(action.Chain, 2, 10*time.Second) +} + +func (tr Chain) submitConsumerAdditionLegacyProposal( + action SubmitConsumerAdditionProposalAction, + verbose bool, ) { spawnTime := tr.testConfig.containerConfig.Now.Add(time.Duration(action.SpawnTime) * time.Millisecond) params := ccvtypes.DefaultParams() @@ -292,6 +399,8 @@ func (tr Chain) submitConsumerAdditionProposal( ValidatorSetCap: action.ValidatorSetCap, Allowlist: action.Allowlist, Denylist: action.Denylist, + MinStake: action.MinStake, + AllowInactiveVals: action.AllowInactiveVals, } bz, err := json.Marshal(prop) @@ -334,7 +443,6 @@ func (tr Chain) submitConsumerAdditionProposal( fmt.Println("submitConsumerAdditionProposal json:", jsonStr) } bz, err = cmd.CombinedOutput() - if err != nil { log.Fatal(err, "\n", string(bz)) } @@ -358,6 +466,75 @@ type SubmitConsumerRemovalProposalAction struct { func (tr Chain) submitConsumerRemovalProposal( action SubmitConsumerRemovalProposalAction, verbose bool, +) { + template := ` + { + "messages": [ + { + "@type": "/interchain_security.ccv.provider.v1.MsgConsumerRemoval", + "chain_id": "%s", + "stop_time": "%s", + "authority": "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn" + } + ], + "metadata": "ipfs://CID", + "deposit": "%dstake", + "title": "%s", + "summary": "It was a great chain", + "expedited": false + } +` + title := fmt.Sprintf("Stop the %v chain", action.ConsumerChain) + stopTime := tr.testConfig.containerConfig.Now.Add(action.StopTimeOffset).Format(time.RFC3339Nano) + + jsonStr := fmt.Sprintf(template, + string(tr.testConfig.chainConfigs[action.ConsumerChain].ChainId), + stopTime, + action.Deposit, + title) + + // #nosec G204 -- bypass unsafe quoting warning (no production code) + proposalFile := "/consumer-removal.proposal" + bz, err := tr.target.ExecCommand( + "/bin/bash", "-c", fmt.Sprintf(`echo '%s' > %s`, jsonStr, proposalFile), + ).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + // CONSUMER REMOVAL PROPOSAL + cmd := tr.target.ExecCommand( + tr.testConfig.chainConfigs[action.Chain].BinaryName, + "tx", "gov", "submit-proposal", proposalFile, + `--from`, `validator`+fmt.Sprint(action.From), + `--chain-id`, string(tr.testConfig.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 { + fmt.Println("submitConsumerRemovalProposal cmd:", cmd.String()) + fmt.Println("submitConsumerRemovalProposal json:", jsonStr) + } + bz, err = cmd.CombinedOutput() + if err != nil { + log.Fatal("submit consumer removal proposal failed:", err, "\n", string(bz)) + } + + if verbose { + fmt.Println("submitConsumerRemovalProposal output:", string(bz)) + } + + // wait for inclusion in a block -> '--broadcast-mode block' is deprecated + tr.waitBlocks(ChainID("provi"), 2, 20*time.Second) +} + +func (tr Chain) submitConsumerRemovalLegacyProposal( + action SubmitConsumerRemovalProposalAction, + verbose bool, ) { stopTime := tr.testConfig.containerConfig.Now.Add(action.StopTimeOffset) prop := client.ConsumerRemovalProposalJSON{ @@ -414,11 +591,95 @@ type SubmitConsumerModificationProposalAction struct { ValidatorSetCap uint32 Allowlist []string Denylist []string + AllowInactiveVals bool + MinStake uint64 } func (tr Chain) submitConsumerModificationProposal( action SubmitConsumerModificationProposalAction, verbose bool, +) { + template := ` + +{ +"messages": [ + { + "@type": "/interchain_security.ccv.provider.v1.MsgConsumerModification", + "title": "Propose the modification of the PSS parameters of a chain", + "description": "description of the consumer modification proposal", + "chain_id": "%s", + "top_N": %d, + "validators_power_cap": %d, + "validator_set_cap": %d, + "allowlist": %s, + "denylist": %s, + "authority": "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn", + "min_stake": %d, + "allow_inactive_vals": %t + } + ], +"metadata": "ipfs://CID", +"deposit": "%sstake", +"title": "Propose the modification of the PSS parameters of a chain", +"summary": "summary of a modification proposal", +"expedited": false + } +` + + jsonStr := fmt.Sprintf(template, + string(tr.testConfig.chainConfigs[action.ConsumerChain].ChainId), + action.TopN, + action.ValidatorsPowerCap, + action.ValidatorSetCap, + action.Allowlist, + action.Denylist, + action.Deposit, + action.MinStake, + action.AllowInactiveVals, + ) + + // #nosec G204 -- bypass unsafe quoting warning (no production code) + proposalFile := "/consumer-mod.proposal" + bz, err := tr.target.ExecCommand( + "/bin/bash", "-c", fmt.Sprintf(`echo '%s' > %s`, jsonStr, proposalFile), + ).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + // CONSUMER MODIFICATION PROPOSAL + cmd := tr.target.ExecCommand( + tr.testConfig.chainConfigs[action.Chain].BinaryName, + "tx", "gov", "submit-proposal", proposalFile, + `--from`, `validator`+fmt.Sprint(action.From), + `--chain-id`, string(tr.testConfig.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 { + fmt.Println("submitConsumerModificationProposal cmd:", cmd.String()) + fmt.Println("submitConsumerModificationProposal json:", jsonStr) + } + bz, err = cmd.CombinedOutput() + if err != nil { + log.Fatal("submit consumer modification proposal failed:", err, "\n", string(bz)) + } + + if verbose { + fmt.Println("submitConsumerModificationProposal output:", string(bz)) + } + + // wait for inclusion in a block -> '--broadcast-mode block' is deprecated + tr.waitBlocks(ChainID("provi"), 2, 10*time.Second) +} + +func (tr Chain) submitConsumerModificationLegacyProposal( + action SubmitConsumerModificationProposalAction, + verbose bool, ) { prop := client.ConsumerModificationProposalJSON{ Title: "Propose the modification of the PSS parameters of a chain", @@ -1000,17 +1261,16 @@ func (tr Chain) addChainToGorelayer( } addChainCommand := tr.target.ExecCommand("rly", "chains", "add", "--file", chainConfigFileName, string(ChainId)) - e2e.ExecuteCommand(addChainCommand, "add chain") + e2e.ExecuteCommand(addChainCommand, "add chain", verbose) keyRestoreCommand := tr.target.ExecCommand("rly", "keys", "restore", string(ChainId), "default", tr.testConfig.validatorConfigs[action.Validator].Mnemonic) - e2e.ExecuteCommand(keyRestoreCommand, "restore keys") + e2e.ExecuteCommand(keyRestoreCommand, "restore keys", verbose) } func (tr Chain) addChainToHermes( action AddChainToRelayerAction, verbose bool, ) { - bz, err := tr.target.ExecCommand("bash", "-c", "hermes", "version").CombinedOutput() if err != nil { log.Fatal(err, "\n error getting hermes version", string(bz)) @@ -1109,7 +1369,7 @@ func (tr Chain) addIbcConnectionGorelayer( //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. pathConfigCommand := tr.target.ExecCommand("bash", "-c", bashCommand) - e2e.ExecuteCommand(pathConfigCommand, "add path config") + e2e.ExecuteCommand(pathConfigCommand, "add path config", verbose) //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. newPathCommand := tr.target.ExecCommand("rly", @@ -1120,12 +1380,12 @@ func (tr Chain) addIbcConnectionGorelayer( "--file", pathConfigFileName, ) - e2e.ExecuteCommand(newPathCommand, "new path") + e2e.ExecuteCommand(newPathCommand, "new path", verbose) //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. newClientsCommand := tr.target.ExecCommand("rly", "transact", "clients", pathName) - e2e.ExecuteCommand(newClientsCommand, "new clients") + e2e.ExecuteCommand(newClientsCommand, "new clients", verbose) tr.waitBlocks(action.ChainA, 1, 10*time.Second) tr.waitBlocks(action.ChainB, 1, 10*time.Second) @@ -1133,7 +1393,7 @@ func (tr Chain) addIbcConnectionGorelayer( //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. newConnectionCommand := tr.target.ExecCommand("rly", "transact", "connection", pathName) - e2e.ExecuteCommand(newConnectionCommand, "new connection") + e2e.ExecuteCommand(newConnectionCommand, "new connection", verbose) tr.waitBlocks(action.ChainA, 1, 10*time.Second) tr.waitBlocks(action.ChainB, 1, 10*time.Second) @@ -1300,7 +1560,7 @@ func (tr Chain) addIbcChannelGorelayer( "--order", action.Order, "--debug", ) - e2e.ExecuteCommand(cmd, "addChannel") + e2e.ExecuteCommand(cmd, "addChannel", verbose) } func (tr Chain) addIbcChannelHermes( @@ -1373,8 +1633,7 @@ func (tr Chain) transferChannelComplete( log.Fatal("transferChannelComplete is not implemented for rly") } - chanOpenTryCmd := tr.target.ExecCommand("hermes", - "tx", "chan-open-try", + chanOpenTryCmd := tr.target.ExecCommand("hermes", "tx", "chan-open-try", "--dst-chain", string(tr.testConfig.chainConfigs[action.ChainB].ChainId), "--src-chain", string(tr.testConfig.chainConfigs[action.ChainA].ChainId), "--dst-connection", "connection-"+fmt.Sprint(action.ConnectionA), @@ -1382,7 +1641,8 @@ func (tr Chain) transferChannelComplete( "--src-port", action.PortA, "--src-channel", "channel-"+fmt.Sprint(action.ChannelA), ) - e2e.ExecuteCommand(chanOpenTryCmd, "transferChanOpenTry") + + e2e.ExecuteCommand(chanOpenTryCmd, "transferChanOpenTry", verbose) chanOpenAckCmd := tr.target.ExecCommand("hermes", "tx", "chan-open-ack", @@ -1395,7 +1655,7 @@ func (tr Chain) transferChannelComplete( "--src-channel", "channel-"+fmt.Sprint(action.ChannelB), ) - e2e.ExecuteCommand(chanOpenAckCmd, "transferChanOpenAck") + e2e.ExecuteCommand(chanOpenAckCmd, "transferChanOpenAck", verbose) chanOpenConfirmCmd := tr.target.ExecCommand("hermes", "tx", "chan-open-confirm", @@ -1407,7 +1667,7 @@ func (tr Chain) transferChannelComplete( "--dst-channel", "channel-"+fmt.Sprint(action.ChannelB), "--src-channel", "channel-"+fmt.Sprint(action.ChannelA), ) - e2e.ExecuteCommand(chanOpenConfirmCmd, "transferChanOpenConfirm") + e2e.ExecuteCommand(chanOpenConfirmCmd, "transferChanOpenConfirm", verbose) } type RelayPacketsAction struct { @@ -1916,7 +2176,7 @@ func (tr Chain) registerRepresentative( panic(fmt.Sprintf("failed writing ccv consumer file : %v", err)) } defer file.Close() - err = os.WriteFile(file.Name(), []byte(fileContent), 0600) + err = os.WriteFile(file.Name(), []byte(fileContent), 0o600) if err != nil { log.Fatalf("Failed writing consumer genesis to file: %v", err) } @@ -1962,13 +2222,78 @@ func (tr Chain) registerRepresentative( } type SubmitChangeRewardDenomsProposalAction struct { + Chain ChainID Denom string Deposit uint From ValidatorID } func (tr Chain) submitChangeRewardDenomsProposal(action SubmitChangeRewardDenomsProposalAction, verbose bool) { - providerChain := tr.testConfig.chainConfigs[ChainID("provi")] + template := ` +{ + "messages": [ + { + "@type": "/interchain_security.ccv.provider.v1.MsgChangeRewardDenoms", + "denoms_to_add": ["%s"], + "denoms_to_remove": ["%s"], + "authority": "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn" + } + ], + "metadata": "ipfs://CID", + "deposit": "%dstake", + "title": "change reward denoms", + "summary": "Proposal to change reward denoms", + "expedited": false +}` + + denomsToAdd := action.Denom + denomsToRemove := "stake" + jsonStr := fmt.Sprintf(template, + denomsToAdd, + denomsToRemove, + action.Deposit) + + //#nosec G204 -- bypass unsafe quoting warning (no production code) + proposalFile := "/change-reward.proposal" + bz, err := tr.target.ExecCommand( + "/bin/bash", "-c", fmt.Sprintf(`echo '%s' > %s`, jsonStr, proposalFile), + ).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + // CHANGE REWARDS DENOM PROPOSAL + cmd := tr.target.ExecCommand( + tr.testConfig.chainConfigs[action.Chain].BinaryName, + "tx", "gov", "submit-proposal", proposalFile, + `--from`, `validator`+fmt.Sprint(action.From), + `--chain-id`, string(tr.testConfig.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 { + fmt.Println("change rewards denom props cmd:", cmd.String()) + fmt.Println("change rewards denom props json:", jsonStr) + } + bz, err = cmd.CombinedOutput() + if err != nil { + log.Fatal("submit-proposal failed:", err, "\n", string(bz)) + } + + if verbose { + fmt.Println("change rewards denom props output:", string(bz)) + } + + // wait for inclusion in a block -> '--broadcast-mode block' is deprecated + tr.waitBlocks(ChainID("provi"), 2, 30*time.Second) +} + +func (tr Chain) submitChangeRewardDenomsLegacyProposal(action SubmitChangeRewardDenomsProposalAction, verbose bool) { + providerChain := tr.testConfig.chainConfigs[action.Chain] prop := client.ChangeRewardDenomsProposalJSON{ Summary: "Change reward denoms", @@ -2393,7 +2718,7 @@ func (tr Chain) optOut(action OptOutAction, target ExecutionTarget, verbose bool } // Use: "opt-out [consumer-chain-id]", - optIn := fmt.Sprintf( + optOut := 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.testConfig.chainConfigs[ChainID("provi")].BinaryName, string(tr.testConfig.chainConfigs[action.Chain].ChainId), @@ -2406,7 +2731,7 @@ func (tr Chain) optOut(action OptOutAction, target ExecutionTarget, verbose bool cmd := target.ExecCommand( "/bin/bash", "-c", - optIn, + optOut, ) if verbose { @@ -2432,6 +2757,71 @@ func (tr Chain) optOut(action OptOutAction, target ExecutionTarget, verbose bool tr.waitBlocks(ChainID("provi"), 2, 30*time.Second) } +type SetConsumerCommissionRateAction struct { + Chain ChainID + Validator ValidatorID + CommissionRate float64 + + // depending on the execution, this action might throw an error (e.g., when no consumer chain exists) + ExpectError bool + ExpectedError string +} + +func (tr Chain) setConsumerCommissionRate(action SetConsumerCommissionRateAction, 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.testConfig.useCometmock { + gas = "9000000" + } + + // Use: "set-consumer-commission-rate [consumer-chain-id] [commission-rate]" + setCommissionRate := fmt.Sprintf( + `%s tx provider set-consumer-commission-rate %s %f --from validator%s --chain-id %s --home %s --node %s --gas %s --keyring-backend test -y -o json`, + tr.testConfig.chainConfigs[ChainID("provi")].BinaryName, + string(tr.testConfig.chainConfigs[action.Chain].ChainId), + action.CommissionRate, + action.Validator, + tr.testConfig.chainConfigs[ChainID("provi")].ChainId, + tr.getValidatorHome(ChainID("provi"), action.Validator), + tr.getValidatorNode(ChainID("provi"), action.Validator), + gas, + ) + + cmd := target.ExecCommand( + "/bin/bash", "-c", + setCommissionRate, + ) + + if verbose { + fmt.Println("setConsumerCommissionRate cmd:", cmd.String()) + } + + bz, err := cmd.CombinedOutput() + if err != nil && !action.ExpectError { + log.Fatalf("unexpected error during commssion rate set - output: %s, err: %s", string(bz), err) + } + + if action.ExpectError && !tr.testConfig.useCometmock { // error report only works with --gas auto, which does not work with CometMock, so ignore + if err == nil || !strings.Contains(string(bz), action.ExpectedError) { + log.Fatalf("expected error not raised: expected: '%s', got '%s'", action.ExpectedError, (bz)) + } + + if verbose { + fmt.Printf("got expected error during commssion rate set | err: %s | output: %s \n", err, string(bz)) + } + } + + if !tr.testConfig.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 commssion rate set | 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) +} + // 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/actions_sovereign_chain.go b/tests/e2e/actions_sovereign_chain.go index 96d308ba57..0c8ccfae56 100644 --- a/tests/e2e/actions_sovereign_chain.go +++ b/tests/e2e/actions_sovereign_chain.go @@ -103,48 +103,91 @@ func (tr Chain) startSovereignChain( }, verbose) } -type LegacyUpgradeProposalAction struct { +type UpgradeProposalAction struct { ChainID ChainID UpgradeTitle string Proposer ValidatorID UpgradeHeight uint64 + Expedited bool } -func (tr *Chain) submitLegacyUpgradeProposal(action LegacyUpgradeProposalAction, verbose bool) { - submit := fmt.Sprintf( - `%s tx gov submit-legacy-proposal software-upgrade %s \ - --title %s \ - --deposit 10000000stake \ - --upgrade-height %s \ - --upgrade-info "perform changeover" \ - --description "perform changeover" \ - --gas 900000 \ - --from validator%s \ - --keyring-backend test \ - --chain-id %s \ - --home %s \ - --node %s \ - --no-validate \ - -y`, - tr.testConfig.chainConfigs[ChainID("sover")].BinaryName, - action.UpgradeTitle, - action.UpgradeTitle, - fmt.Sprint(action.UpgradeHeight), - action.Proposer, - tr.testConfig.chainConfigs[ChainID("sover")].ChainId, - tr.getValidatorHome(ChainID("sover"), action.Proposer), - tr.getValidatorNode(ChainID("sover"), action.Proposer), - ) - cmd := tr.target.ExecCommand("/bin/bash", "-c", submit) +func (tr *Chain) submitUpgradeProposal(action UpgradeProposalAction, verbose bool) { + + // Get authority address + binary := tr.testConfig.chainConfigs[ChainID("sover")].BinaryName + cmd := tr.target.ExecCommand(binary, + "query", "upgrade", "authority", + "--node", tr.getValidatorNode(ChainID("sover"), action.Proposer), + "-o", "json") + bz, err := cmd.CombinedOutput() + if err != nil { + log.Fatalf("failed running command '%s': %v", cmd, err) + } + + var authority struct { + Address string `json:"address"` + } + err = json.Unmarshal(bz, &authority) + if err != nil { + log.Fatalf("Failed getting authority: err=%v, data=%s", err, string(bz)) + } + + // Upgrade Proposal Content + metadata := "ipfs://CID" + deposit := "10000000stake" + summary := "my summary" + proposalJson := fmt.Sprintf(` +{ + "messages": [ + { + "@type": "/cosmos.upgrade.v1beta1.MsgSoftwareUpgrade", + "authority": "%s", + "plan": { + "name": "sovereign-changeover", + "height": "%d", + "info": "my upgrade info", + "upgraded_client_state": null + } + } + ], + "metadata": "%s", + "title": "%s", + "summary": "%s", + "deposit": "%s", + "expedited": %t +}`, authority.Address, action.UpgradeHeight, metadata, action.UpgradeTitle, summary, deposit, action.Expedited) + + //#nosec G204 -- bypass unsafe quoting warning (no production code) + proposalPath := "/temp-proposal.json" + bz, err = tr.target.ExecCommand( + "/bin/bash", "-c", fmt.Sprintf(`echo '%s' > %s`, proposalJson, proposalPath), + ).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + // Submit Proposal + cmd = tr.target.ExecCommand(binary, + "tx", "gov", "submit-proposal", proposalPath, + "--gas", "900000", + "--from", "validator"+string(action.Proposer), + "--keyring-backend", "test", + "--chain-id", string(tr.testConfig.chainConfigs[ChainID("sover")].ChainId), + "--home", tr.getValidatorHome(ChainID("sover"), action.Proposer), + "--node", tr.getValidatorNode(ChainID("sover"), action.Proposer), + "-y") if verbose { - fmt.Println("submitUpgradeProposal cmd:", cmd.String()) + fmt.Println("Submit proposal:", cmd.String()) } - bz, err := cmd.CombinedOutput() + bz, err = cmd.CombinedOutput() if err != nil { log.Fatal(err, "\n", string(bz)) } + if verbose { + log.Println("Response to submit-proposal: ", string(bz)) + } tr.waitBlocks(action.ChainID, 1, 15*time.Second) } diff --git a/tests/e2e/config.go b/tests/e2e/config.go index 40a1cfc4c5..4fa762b5f7 100644 --- a/tests/e2e/config.go +++ b/tests/e2e/config.go @@ -84,15 +84,21 @@ type ( type TestConfigType string const ( - DefaultTestCfg TestConfigType = "default" - ChangeoverTestCfg TestConfigType = "changeover" - DemocracyTestCfg TestConfigType = "democracy" - DemocracyRewardTestCfg TestConfigType = "democracy-reward" - SlashThrottleTestCfg TestConfigType = "slash-throttling" - MulticonsumerTestCfg TestConfigType = "multi-consumer" - ConsumerMisbehaviourTestCfg TestConfigType = "consumer-misbehaviour" - CompatibilityTestCfg TestConfigType = "compatibility" - SmallMaxValidatorsTestCfg TestConfigType = "small-max-validators" + DefaultTestCfg TestConfigType = "default" + ChangeoverTestCfg TestConfigType = "changeover" + DemocracyTestCfg TestConfigType = "democracy" + DemocracyRewardTestCfg TestConfigType = "democracy-reward" + SlashThrottleTestCfg TestConfigType = "slash-throttling" + MulticonsumerTestCfg TestConfigType = "multi-consumer" + ConsumerMisbehaviourTestCfg TestConfigType = "consumer-misbehaviour" + CompatibilityTestCfg TestConfigType = "compatibility" + SmallMaxValidatorsTestCfg TestConfigType = "small-max-validators" + InactiveProviderValsTestCfg TestConfigType = "inactive-provider-vals" + GovTestCfg TestConfigType = "gov" + InactiveValsGovTestCfg TestConfigType = "inactive-vals-gov" + InactiveValsMintTestCfg TestConfigType = "inactive-vals-mint" + MintTestCfg TestConfigType = "mint" + InactiveValsExtraValsTestCfg TestConfigType = "inactive-vals-extra-vals" ) type TestConfig struct { @@ -128,15 +134,24 @@ func (tr *TestConfig) Initialize() { // Note: if no matching version is found an empty string is returned func getIcsVersion(reference string) string { icsVersion := "" - if reference == "" { + + if reference == "" || reference == VLatest { return icsVersion } + if semver.IsValid(reference) { // remove build suffix return semver.Canonical(reference) } - for _, tag := range []string{"v2.0.0", "v2.4.0", "v2.4.0-lsm", "v3.1.0", "v3.2.0", "v3.3.0", "v4.0.0", "v4.1.1", "v4.1.1-lsm"} { + // List of all tags matching vX.Y.Z or vX.Y.Z-lsm in ascending order + cmd := exec.Command("git", "tag", "-l", "--sort", "v:refname", "v*.?", "v*.?-lsm", "v*.??", "v*.??-lsm") + out, err := cmd.CombinedOutput() + if err != nil { + panic(fmt.Sprintf("Error getting sorted tag list from git: %s", err.Error())) + } + icsVersions := strings.Split(string(out), "\n") + for _, tag := range icsVersions { //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments cmd := exec.Command("git", "merge-base", "--is-ancestor", reference, tag) out, err := cmd.CombinedOutput() @@ -181,6 +196,18 @@ func GetTestConfig(cfgType TestConfigType, providerVersion, consumerVersion stri testCfg = CompatibilityTestConfig(pv, cv) case SmallMaxValidatorsTestCfg: testCfg = SmallMaxValidatorsTestConfig() + case InactiveProviderValsTestCfg: + testCfg = InactiveProviderValsTestConfig() + case GovTestCfg: + testCfg = GovTestConfig() + case InactiveValsGovTestCfg: + testCfg = InactiveValsGovTestConfig() + case InactiveValsMintTestCfg: + testCfg = InactiveValsMintTestConfig() + case MintTestCfg: + testCfg = MintTestConfig() + case InactiveValsExtraValsTestCfg: + testCfg = InactiveValsExtraValsTestConfig() default: panic(fmt.Sprintf("Invalid test config: %s", cfgType)) } @@ -565,6 +592,46 @@ func DemocracyTestConfig(allowReward bool) TestConfig { return tr } +func InactiveProviderValsTestConfig() TestConfig { + tr := DefaultTestConfig() + tr.name = "InactiveValsConfig" + // set the MaxProviderConsensusValidators param to 2 + proviConfig := tr.chainConfigs[ChainID("provi")] + proviConfig.GenesisChanges += " | .app_state.provider.params.max_provider_consensus_validators = \"2\"" + + consuConfig := tr.chainConfigs[ChainID("consu")] + // set the soft_opt_out threshold to 0% to make sure all validators are slashed for downtime + consuConfig.GenesisChanges += " | .app_state.ccvconsumer.params.soft_opt_out_threshold = \"0.0\"" + tr.chainConfigs[ChainID("provi")] = proviConfig + tr.chainConfigs[ChainID("consu")] = consuConfig + + // make it so that carol does not use a consumer key + carolConfig := tr.validatorConfigs[ValidatorID("carol")] + carolConfig.UseConsumerKey = false + tr.validatorConfigs[ValidatorID("carol")] = carolConfig + + return tr +} + +func InactiveValsExtraValsTestConfig() TestConfig { + tr := InactiveProviderValsTestConfig() + + // set the MaxProviderConsensusValidators param to 4 + proviConfig := tr.chainConfigs[ChainID("provi")] + proviConfig.GenesisChanges += " | .app_state.provider.params.max_provider_consensus_validators = \"4\"" + // set max validators to 5 + proviConfig.GenesisChanges += " | .app_state.staking.params.max_validators = \"5\"" + tr.chainConfigs[ChainID("provi")] = proviConfig + + // add the extra validators to the validator config + extraVals := GetExtraValidatorConfigs() + for valId, val := range extraVals { + tr.validatorConfigs[valId] = val + } + + return tr +} + func SmallMaxValidatorsTestConfig() TestConfig { cfg := DefaultTestConfig() @@ -581,6 +648,60 @@ func SmallMaxValidatorsTestConfig() TestConfig { return cfg } +func GovTestConfig() TestConfig { + cfg := DefaultTestConfig() + + // set the quorum to 50% + proviConfig := cfg.chainConfigs[ChainID("provi")] + proviConfig.GenesisChanges += "| .app_state.gov.params.quorum = \"0.5\"" + 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 InactiveValsGovTestConfig() TestConfig { + cfg := GovTestConfig() + + // set the MaxValidators to 1 + proviConfig := cfg.chainConfigs[ChainID("provi")] + proviConfig.GenesisChanges += "| .app_state.staking.params.max_validators = 1" + cfg.chainConfigs[ChainID("provi")] = proviConfig + + return cfg +} + +func MintTestConfig() TestConfig { + cfg := GovTestConfig() + AdjustMint(cfg) + + return cfg +} + +func InactiveValsMintTestConfig() TestConfig { + cfg := InactiveValsGovTestConfig() + AdjustMint(cfg) + + return cfg +} + +// AdjustMint adjusts the mint parameters to have a very low goal bonded amount +// and a high inflation rate change +func AdjustMint(cfg TestConfig) { + proviConfig := cfg.chainConfigs[ChainID("provi")] + // total supply is 30000000000stake; we want to set the mint bonded goal to + // a small fraction of that + proviConfig.GenesisChanges += "| .app_state.mint.params.goal_bonded = \"0.001\"" + + "| .app_state.mint.params.inflation_rate_change = \"1\"" + + "| .app_state.mint.params.inflation_max = \"0.5\"" + + "| .app_state.mint.params.inflation_min = \"0.1\"" + cfg.chainConfigs[ChainID("provi")] = proviConfig +} + func MultiConsumerTestConfig() TestConfig { tr := TestConfig{ name: string(MulticonsumerTestCfg), @@ -1175,3 +1296,119 @@ func GetHermesConfig(hermesVersion, queryNodeIP string, chainCfg ChainConfig, is } return hermesConfig } + +// GetExtraValidatorConfigs returns a map of extra validator configurations. +// These are configurations that are not part of the default configurations, +// for cases where more validators are needed. +// Commented out fields are fields that can be set, but are not necessary for the test +// they are used in so far. +// These are left as guidance to fill out as they become relevant in the future. +func GetExtraValidatorConfigs() map[ValidatorID]ValidatorConfig { + return map[ValidatorID]ValidatorConfig{ + ValidatorID("david"): { + Mnemonic: "save arm pill nothing riot park analyst fever couple use reform hotel involve captain ride spell cricket spoil admit proud file renew below add", + DelAddress: "cosmos1jv9j37zakskecthedez2xuvkd7aj4v96u6wm57", + // DelAddressOnConsumer: "consumer1dkas8mu4kyhl5jrh4nzvm65qz588hy9qahzgv6", + ValoperAddress: "cosmosvaloper1jv9j37zakskecthedez2xuvkd7aj4v96ew6wcd", + // ValoperAddressOnConsumer: "consumervaloper1dkas8mu4kyhl5jrh4nzvm65qz588hy9qj0phzw", + ValconsAddress: "cosmosvalcons1vde2tkme0336durkp7qlehyw2v0r2rgm5lfcw5", + ValconsAddressOnConsumer: "consumervalcons1vde2tkme0336durkp7qlehyw2v0r2rgmmxnal5", + PrivValidatorKey: `{ + "address": "6372A5DB797C63A6F0760F81FCDC8E531E350D1B", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "JFt8aKr1AnubC23rVEUza0pl3DQC0VdC6jUlnkjCh5o=" + }, + "priv_key": { + "type": "tendermint/PrivKeyEd25519", + "value": "mBErI1aFTt2VHNiWkb14mEfSIfUU6PHndJCKaJ2XjkwkW3xoqvUCe5sLbetURTNrSmXcNALRV0LqNSWeSMKHmg==" + } + }`, + NodeKey: `{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"LJQ/VDAYtEEaJC3NA32fZ2oLLm9akLeLVnBlJ2WOYEMLohbwfac0x42Qh23E/ByEMliSjfGvLFQZIYzRqwJcLA=="}}`, + IpSuffix: "7", + + // // consumer chain assigned key + // ConsumerMnemonic: "grunt list hour endless observe better spoil penalty lab duck only layer vague fantasy satoshi record demise topple space shaft solar practice donor sphere", + // ConsumerDelAddress: "consumer1q90l6j6lzzgt460ehjj56azknlt5yrd44y2uke", + // ConsumerDelAddressOnProvider: "cosmos1q90l6j6lzzgt460ehjj56azknlt5yrd4s38n97", + // ConsumerValoperAddress: "consumervaloper1q90l6j6lzzgt460ehjj56azknlt5yrd46ufrcd", + // ConsumerValoperAddressOnProvider: "cosmosvaloper1q90l6j6lzzgt460ehjj56azknlt5yrd449nxfd", + // ConsumerValconsAddress: "consumervalcons1uuec3cjxajv5te08p220usrjhkfhg9wyref26m", + // ConsumerValconsAddressOnProvider: "cosmosvalcons1uuec3cjxajv5te08p220usrjhkfhg9wyvqn0tm", + // ConsumerValPubKey: `{"@type":"/cosmos.crypto.ed25519.PubKey","key":"QlG+iYe6AyYpvY1z9RNJKCVlH14Q/qSz4EjGdGCru3o="}`, + // ConsumerPrivValidatorKey: `{"address":"E73388E246EC9945E5E70A94FE4072BD937415C4","pub_key":{"type":"tendermint/PubKeyEd25519","value":"QlG+iYe6AyYpvY1z9RNJKCVlH14Q/qSz4EjGdGCru3o="},"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"OFR4w+FC6EMw5fAGTrHVexyPrjzQ7QfqgZOMgVf0izlCUb6Jh7oDJim9jXP1E0koJWUfXhD+pLPgSMZ0YKu7eg=="}}`, + // ConsumerNodeKey: `{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"uhPCqnL2KE8m/8OFNLQ5bN3CJr6mds+xfBi0E4umT/s2uWiJhet+vbYx88DHSdof3gGFNTIzAIxSppscBKX96w=="}}`, + // UseConsumerKey: false, + }, + ValidatorID("eve"): { + Mnemonic: "board lava muffin daughter frozen chimney chest whale give subject inquiry forward alter gasp busy flee wire ecology invite code comic cloth shoot pen", + DelAddress: "cosmos1p56m290cgys4396e3f8p4kj9lrzwak7zemg45t", + // DelAddressOnConsumer: "consumer1dkas8mu4kyhl5jrh4nzvm65qz588hy9qahzgv6", + ValoperAddress: "cosmosvaloper1p56m290cgys4396e3f8p4kj9lrzwak7zu0uqcc", + // ValoperAddressOnConsumer: "consumervaloper1dkas8mu4kyhl5jrh4nzvm65qz588hy9qj0phzw", + ValconsAddress: "cosmosvalcons1dqvy6lz440hj4zxjske5knsyx60ac5estqx6k2", + ValconsAddressOnConsumer: "consumervalcons1dqvy6lz440hj4zxjske5knsyx60ac5esyeul82", + PrivValidatorKey: `{ + "address": "68184D7C55ABEF2A88D285B34B4E04369FDC5330", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "QbLLxm/mNHfS9WWXTxvt30D2xeC7/HRrMrZJIVOtj9s=" + }, + "priv_key": { + "type": "tendermint/PrivKeyEd25519", + "value": "LDPp4B9/Q18yZBJv2zXMnCA+NB9wvaM3XAkWLuCvbaFBssvGb+Y0d9L1ZZdPG+3fQPbF4Lv8dGsytkkhU62P2w==" + } + }`, + NodeKey: `{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"eyDlEXWWP5KwD2IKZeJ/bvf8jT+pVsXYVjpV2HfP+GEjngJe2dNbuuNqtC6L7SFcp5/W2aOIKsslASqv+Oai9Q=="}}`, + IpSuffix: "8", + + // // consumer chain assigned key + // ConsumerMnemonic: "grunt list hour endless observe better spoil penalty lab duck only layer vague fantasy satoshi record demise topple space shaft solar practice donor sphere", + // ConsumerDelAddress: "consumer1q90l6j6lzzgt460ehjj56azknlt5yrd44y2uke", + // ConsumerDelAddressOnProvider: "cosmos1q90l6j6lzzgt460ehjj56azknlt5yrd4s38n97", + // ConsumerValoperAddress: "consumervaloper1q90l6j6lzzgt460ehjj56azknlt5yrd46ufrcd", + // ConsumerValoperAddressOnProvider: "cosmosvaloper1q90l6j6lzzgt460ehjj56azknlt5yrd449nxfd", + // ConsumerValconsAddress: "consumervalcons1uuec3cjxajv5te08p220usrjhkfhg9wyref26m", + // ConsumerValconsAddressOnProvider: "cosmosvalcons1uuec3cjxajv5te08p220usrjhkfhg9wyvqn0tm", + // ConsumerValPubKey: `{"@type":"/cosmos.crypto.ed25519.PubKey","key":"QlG+iYe6AyYpvY1z9RNJKCVlH14Q/qSz4EjGdGCru3o="}`, + // ConsumerPrivValidatorKey: `{"address":"E73388E246EC9945E5E70A94FE4072BD937415C4","pub_key":{"type":"tendermint/PubKeyEd25519","value":"QlG+iYe6AyYpvY1z9RNJKCVlH14Q/qSz4EjGdGCru3o="},"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"OFR4w+FC6EMw5fAGTrHVexyPrjzQ7QfqgZOMgVf0izlCUb6Jh7oDJim9jXP1E0koJWUfXhD+pLPgSMZ0YKu7eg=="}}`, + // ConsumerNodeKey: `{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"uhPCqnL2KE8m/8OFNLQ5bN3CJr6mds+xfBi0E4umT/s2uWiJhet+vbYx88DHSdof3gGFNTIzAIxSppscBKX96w=="}}`, + // UseConsumerKey: false, + }, + ValidatorID("fred"): { + Mnemonic: "squeeze runway ivory cause throw diagram camp cricket clutch lens venture panel explain transfer dove notice nest twist plate van paddle rude summer give", + DelAddress: "cosmos13s90cyesdm2292pn8mnzmjm0ez3nd7jaw32tdq", + // // DelAddressOnConsumer: "consumer1dkas8mu4kyhl5jrh4nzvm65qz588hy9qahzgv6", + ValoperAddress: "cosmosvaloper13s90cyesdm2292pn8mnzmjm0ez3nd7jat977pn", + // // ValoperAddressOnConsumer: "consumervaloper1dkas8mu4kyhl5jrh4nzvm65qz588hy9qj0phzw", + ValconsAddress: "cosmosvalcons1xvuktnaz3rvwmldw7ktv7lcn2xf7l252wmsv5e", + ValconsAddressOnConsumer: "consumervalcons1xvuktnaz3rvwmldw7ktv7lcn2xf7l252pz2f9e", + PrivValidatorKey: `{ + "address": "333965CFA288D8EDFDAEF596CF7F135193EFAA8A", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "nC7g+8/y3NNx7D6Ae970H9954JeqX7SyAxNHh5GnJGs=" + }, + "priv_key": { + "type": "tendermint/PrivKeyEd25519", + "value": "otxstGMSCO0T4CU/Ouxxaam+HUFoL9ArKmMqvSaaCaCcLuD7z/Lc03HsPoB73vQf33ngl6pftLIDE0eHkackaw==" + } + }`, + NodeKey: `{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"oeJWEaFbLHIgZEbCeVDeYKDqM23fv3j/FobYdhIffQYN2X9MUvBlkhi4Uz6dLQ+vSfZIZb2x2vPcgJsCUpLGnQ=="}}`, + IpSuffix: "9", + + // // consumer chain assigned key + // ConsumerMnemonic: "grunt list hour endless observe better spoil penalty lab duck only layer vague fantasy satoshi record demise topple space shaft solar practice donor sphere", + // ConsumerDelAddress: "consumer1q90l6j6lzzgt460ehjj56azknlt5yrd44y2uke", + // ConsumerDelAddressOnProvider: "cosmos1q90l6j6lzzgt460ehjj56azknlt5yrd4s38n97", + // ConsumerValoperAddress: "consumervaloper1q90l6j6lzzgt460ehjj56azknlt5yrd46ufrcd", + // ConsumerValoperAddressOnProvider: "cosmosvaloper1q90l6j6lzzgt460ehjj56azknlt5yrd449nxfd", + // ConsumerValconsAddress: "consumervalcons1uuec3cjxajv5te08p220usrjhkfhg9wyref26m", + // ConsumerValconsAddressOnProvider: "cosmosvalcons1uuec3cjxajv5te08p220usrjhkfhg9wyvqn0tm", + // ConsumerValPubKey: `{"@type":"/cosmos.crypto.ed25519.PubKey","key":"QlG+iYe6AyYpvY1z9RNJKCVlH14Q/qSz4EjGdGCru3o="}`, + // ConsumerPrivValidatorKey: `{"address":"E73388E246EC9945E5E70A94FE4072BD937415C4","pub_key":{"type":"tendermint/PubKeyEd25519","value":"QlG+iYe6AyYpvY1z9RNJKCVlH14Q/qSz4EjGdGCru3o="},"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"OFR4w+FC6EMw5fAGTrHVexyPrjzQ7QfqgZOMgVf0izlCUb6Jh7oDJim9jXP1E0koJWUfXhD+pLPgSMZ0YKu7eg=="}}`, + // ConsumerNodeKey: `{"priv_key":{"type":"tendermint/PrivKeyEd25519","value":"uhPCqnL2KE8m/8OFNLQ5bN3CJr6mds+xfBi0E4umT/s2uWiJhet+vbYx88DHSdof3gGFNTIzAIxSppscBKX96w=="}}`, + // UseConsumerKey: false, + }, + } +} diff --git a/tests/e2e/json_utils.go b/tests/e2e/json_utils.go index e1e0b67fc6..4d7d2bbee7 100644 --- a/tests/e2e/json_utils.go +++ b/tests/e2e/json_utils.go @@ -217,7 +217,7 @@ func UnmarshalMapToActionType(rawAction json.RawMessage, actionTypeString string return a, nil } case "main.LegacyUpgradeProposalAction": - var a LegacyUpgradeProposalAction + var a UpgradeProposalAction err := json.Unmarshal(rawAction, &a) if err == nil { return a, nil diff --git a/tests/e2e/main.go b/tests/e2e/main.go index d0b13ddd94..2864c9b413 100644 --- a/tests/e2e/main.go +++ b/tests/e2e/main.go @@ -204,6 +204,54 @@ var stepChoices = map[string]StepChoice{ 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, }, + "inactive-provider-validators-on-consumer": { + name: "inactive-provider-validators-on-consumer", + steps: stepsInactiveProviderValidators(), + description: "test inactive validators on consumer", + testConfig: InactiveProviderValsTestCfg, + }, + "inactive-vals-topN": { + name: "inactive-vals-topN", + steps: stepsInactiveValsWithTopN(), + description: "test inactive validators on topN chain", + testConfig: InactiveProviderValsTestCfg, + }, + "inactive-provider-validators-governance": { + name: "inactive-provider-validators-governance", + steps: stepsInactiveProviderValidatorsGovernance(), + description: "test governance with inactive validators", + testConfig: InactiveValsGovTestCfg, + }, + "inactive-provider-validators-governance-basecase": { + name: "inactive-provider-validators-governance-basecase", + steps: stepsInactiveProviderValidatorsGovernanceBasecase(), + description: "comparison for governance when there are *no* inactive validators, to verify the difference to the governance test *with* inactive validators", + testConfig: GovTestCfg, + }, + "min-stake": { + name: "min-stake", + steps: stepsMinStake(), + description: "checks that the min stake parameter for consumer chains is respected", + testConfig: GovTestCfg, // see above: we reuse the GovTestCfg for convenience + }, + "inactive-vals-mint": { + name: "inactive-vals-mint", + steps: stepsInactiveValsMint(), + description: "test minting with inactive validators", + testConfig: InactiveValsMintTestCfg, + }, + "mint-basecase": { + name: "mint-basecase", + steps: stepsMintBasecase(), + description: "test minting without inactive validators as a sanity check", + testConfig: MintTestCfg, + }, + "inactive-vals-outside-max-validators": { + name: "inactive-vals-outside-max-validators", + steps: stepsInactiveValsTopNReproduce(), + description: "tests the behaviour of inactive validators with a top N = 100 chain and when max_validators is smaller than the total number of validators", + testConfig: InactiveValsExtraValsTestCfg, + }, } func getTestCaseUsageString() string { @@ -286,14 +334,27 @@ func getTestCases(selectedPredefinedTests, selectedTestFiles TestSet, providerVe // Run default tests if no test cases were selected if len(selectedPredefinedTests) == 0 && len(selectedTestFiles) == 0 { selectedPredefinedTests = TestSet{ - "changeover", "happy-path", - "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", + "changeover", + "happy-path", + "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", "partial-set-security-modification-proposal", "active-set-changes", + "inactive-provider-validators-on-consumer", + "inactive-vals-topN", + "inactive-provider-validators-governance", + "min-stake", + "inactive-vals-mint", } if includeMultiConsumer != nil && *includeMultiConsumer { selectedPredefinedTests = append(selectedPredefinedTests, "multiconsumer") diff --git a/tests/e2e/state.go b/tests/e2e/state.go index b9618a3964..f29c8f59f0 100644 --- a/tests/e2e/state.go +++ b/tests/e2e/state.go @@ -116,6 +116,34 @@ func (tr Chain) GetChainState(chain ChainID, modelState ChainState) ChainState { chainState.HasToValidate = &hasToValidate } + if modelState.InflationRateChange != nil { + // get the inflation rate now + inflationRateNow := tr.target.GetInflationRate(chain) + + // wait a block + tr.waitBlocks(chain, 1, 10*time.Second) + + // get the new inflation rate + inflationRateAfter := tr.target.GetInflationRate(chain) + + // calculate the change + inflationRateChange := inflationRateAfter - inflationRateNow + var inflationRateChangeDirection int + if inflationRateChange > 0 { + inflationRateChangeDirection = 1 + } else if inflationRateChange < 0 { + inflationRateChangeDirection = -1 + } else { + inflationRateChangeDirection = 0 + } + chainState.InflationRateChange = &inflationRateChangeDirection + } + + if modelState.ConsumerCommissionRates != nil { + consumerCommissionRates := tr.GetConsumerCommissionRates(chain, *modelState.ConsumerCommissionRates) + chainState.ConsumerCommissionRates = &consumerCommissionRates + } + if modelState.ConsumerPendingPacketQueueSize != nil { pendingPacketQueueSize := tr.target.GetPendingPacketQueueSize(chain) chainState.ConsumerPendingPacketQueueSize = &pendingPacketQueueSize @@ -295,13 +323,17 @@ func (tr Chain) curlJsonRPCRequest(method, params, address string) { cmd := tr.target.ExecCommand("bash", "-c", fmt.Sprintf(cmd_template, method, params, address)) verbosity := false - e2e.ExecuteCommandWithVerbosity(cmd, "curlJsonRPCRequest", verbosity) + e2e.ExecuteCommand(cmd, "curlJsonRPCRequest", verbosity) } func uintPtr(i uint) *uint { return &i } +func intPtr(i int) *int { + return &i +} + type Commands struct { containerConfig ContainerConfig // FIXME only needed for 'Now' time tracking validatorConfigs map[ValidatorID]ValidatorConfig @@ -357,16 +389,21 @@ func (tr Commands) GetReward(chain ChainID, validator ValidatorID, blockHeight u binaryName := tr.chainConfigs[chain].BinaryName cmd := tr.target.ExecCommand(binaryName, - "query", "distribution", "delegation-total-rewards", - "--delegator-address", delAddresss, + "query", "distribution", "rewards", + delAddresss, `--height`, fmt.Sprint(blockHeight), `--node`, tr.GetQueryNode(chain), `-o`, `json`, ) - bz, err := cmd.CombinedOutput() + if *verbose { + log.Println("getting rewards for chain: ", chain, " validator: ", validator, " blockHeight: ", blockHeight) + log.Println(cmd) + } + bz, err := cmd.CombinedOutput() if err != nil { + log.Println("running cmd: ", cmd) log.Fatal("failed getting rewards: ", err, "\n", string(bz)) } @@ -380,39 +417,42 @@ func (tr Commands) GetReward(chain ChainID, validator ValidatorID, blockHeight u // interchain-securityd query gov proposals func (tr Commands) GetProposal(chain ChainID, proposal uint) Proposal { - var noProposalRegex = regexp.MustCompile(`doesn't exist: key not found`) + noProposalRegex := regexp.MustCompile(`doesn't exist: key not found`) binaryName := tr.chainConfigs[chain].BinaryName - bz, err := tr.target.ExecCommand(binaryName, + cmd := tr.target.ExecCommand(binaryName, "query", "gov", "proposal", fmt.Sprint(proposal), `--node`, tr.GetQueryNode(chain), - `-o`, `json`, - ).CombinedOutput() + `-o`, `json`) + bz, err := cmd.CombinedOutput() + if err != nil { + log.Println("Error getting governance proposal", proposal, "\n\t cmd: ", cmd, "\n\t err:", err, "\n\t out: ", string(bz)) + } prop := TextProposal{} - + propRaw := string(bz) if err != nil { if noProposalRegex.Match(bz) { return prop } - log.Fatal(err, "\n", string(bz)) + log.Fatal(err, "\n", propRaw) } // for legacy proposal types submitted using "tx submit-legacyproposal" (cosmos-sdk/v1/MsgExecLegacyContent) - propType := gjson.Get(string(bz), `proposal.messages.0.value.content.type`).String() - rawContent := gjson.Get(string(bz), `proposal.messages.0.value.content.value`) + propType := gjson.Get(propRaw, `proposal.messages.0.value.content.type`).String() + rawContent := gjson.Get(propRaw, `proposal.messages.0.value.content.value`) // for current (>= v47) prop types submitted using "tx submit-proposal" if propType == "" { - propType = gjson.Get(string(bz), `proposal.messages.0.type`).String() - rawContent = gjson.Get(string(bz), `proposal.messages.0.value`) + propType = gjson.Get(propRaw, `proposal.messages.0.type`).String() + rawContent = gjson.Get(propRaw, `proposal.messages.0.value`) } - title := gjson.Get(string(bz), `proposal.title`).String() - deposit := gjson.Get(string(bz), `proposal.total_deposit.#(denom=="stake").amount`).Uint() - status := gjson.Get(string(bz), `proposal.status`).String() + title := gjson.Get(propRaw, `proposal.title`).String() + deposit := gjson.Get(propRaw, `proposal.total_deposit.#(denom=="stake").amount`).Uint() + status := gjson.Get(propRaw, `proposal.status`).String() switch propType { case "/cosmos.gov.v1beta1.TextProposal": @@ -425,7 +465,7 @@ func (tr Commands) GetProposal(chain ChainID, proposal uint) Proposal { Title: title, Description: description, } - case "/interchain_security.ccv.provider.v1.ConsumerAdditionProposal": + case "/interchain_security.ccv.provider.v1.MsgConsumerAddition": chainId := rawContent.Get("chain_id").String() spawnTime := rawContent.Get("spawn_time").Time().Sub(tr.containerConfig.Now) @@ -448,6 +488,7 @@ func (tr Commands) GetProposal(chain ChainID, proposal uint) Proposal { }, } case "/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal": + case "cosmos-sdk/MsgSoftwareUpgrade": height := rawContent.Get("plan.height").Uint() title := rawContent.Get("plan.name").String() return UpgradeProposal{ @@ -457,7 +498,7 @@ func (tr Commands) GetProposal(chain ChainID, proposal uint) Proposal { Title: title, Type: "/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal", } - case "/interchain_security.ccv.provider.v1.ConsumerRemovalProposal": + case "/interchain_security.ccv.provider.v1.MsgConsumerRemoval": chainId := rawContent.Get("chain_id").String() stopTime := rawContent.Get("stop_time").Time().Sub(tr.containerConfig.Now) @@ -478,7 +519,7 @@ func (tr Commands) GetProposal(chain ChainID, proposal uint) Proposal { case "/ibc.applications.transfer.v1.MsgUpdateParams": var params IBCTransferParams if err := json.Unmarshal([]byte(rawContent.Get("params").String()), ¶ms); err != nil { - log.Fatal("cannot unmarshal ibc-transfer params: ", err, "\n", string(bz)) + log.Fatal("cannot unmarshal ibc-transfer params: ", err, "\n", propRaw) } return IBCTransferParamsProposal{ @@ -488,7 +529,7 @@ func (tr Commands) GetProposal(chain ChainID, proposal uint) Proposal { Params: params, } - case "/interchain_security.ccv.provider.v1.ConsumerModificationProposal": + case "/interchain_security.ccv.provider.v1.MsgConsumerModification": chainId := rawContent.Get("chain_id").String() var chain ChainID @@ -508,13 +549,13 @@ func (tr Commands) GetProposal(chain ChainID, proposal uint) Proposal { return ParamsProposal{ Deposit: uint(deposit), Status: status, - Subspace: gjson.Get(string(bz), `messages.0.content.changes.0.subspace`).String(), - Key: gjson.Get(string(bz), `messages.0.content.changes.0.key`).String(), - Value: gjson.Get(string(bz), `messages.0.content.changes.0.value`).String(), + Subspace: gjson.Get(propRaw, `messages.0.content.changes.0.subspace`).String(), + Key: gjson.Get(propRaw, `messages.0.content.changes.0.key`).String(), + Value: gjson.Get(propRaw, `messages.0.content.changes.0.value`).String(), } } - log.Fatal("received unknown proposal type: ", propType, "proposal JSON:", string(bz)) + log.Fatal("received unknown proposal type: '", propType, "', proposal JSON:", propRaw) return nil } @@ -863,6 +904,23 @@ func (tr Commands) GetHasToValidate( return chains } +func (tr Commands) GetInflationRate( + chain ChainID, +) float64 { + binaryName := tr.chainConfigs[chain].BinaryName + bz, err := tr.target.ExecCommand(binaryName, + "query", "mint", "inflation", + `--node`, tr.GetQueryNode(chain), + `-o`, `json`, + ).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + inflationRate := gjson.Get(string(bz), "inflation").Float() + return inflationRate +} + func (tr Commands) GetTrustedHeight( chain ChainID, clientID string, @@ -951,3 +1009,33 @@ func (tr Commands) GetQueryNodeIP(chain ChainID) string { } return fmt.Sprintf("%s.253", tr.chainConfigs[chain].IpPrefix) } + +// GetConsumerCommissionRate returns the commission rate of the given validator on the given consumerChain +func (tr Commands) GetConsumerCommissionRate(consumerChain ChainID, validator ValidatorID) float64 { + binaryName := tr.chainConfigs[ChainID("provi")].BinaryName + cmd := tr.target.ExecCommand(binaryName, + "query", "provider", "validator-consumer-commission-rate", + string(consumerChain), tr.validatorConfigs[validator].ValconsAddress, + `--node`, tr.GetQueryNode(ChainID("provi")), + `-o`, `json`, + ) + bz, err := cmd.CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + rate, err := strconv.ParseFloat(gjson.Get(string(bz), "rate").String(), 64) + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + return rate +} + +func (tr Chain) GetConsumerCommissionRates(chain ChainID, modelState map[ValidatorID]float64) map[ValidatorID]float64 { + actualState := map[ValidatorID]float64{} + for k := range modelState { + actualState[k] = tr.target.GetConsumerCommissionRate(chain, k) + } + + return actualState +} diff --git a/tests/e2e/steps.go b/tests/e2e/steps.go index 5e16a61776..279b312e7f 100644 --- a/tests/e2e/steps.go +++ b/tests/e2e/steps.go @@ -1,5 +1,12 @@ package main +import ( + "strconv" + + gov "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" +) + type Step struct { Action interface{} State State @@ -138,3 +145,134 @@ var consumerDoubleDowntimeSteps = concatSteps( stepsRedelegate("consu"), stepsDoubleDowntime("consu"), ) + +func stepsInactiveValsTopNReproduce() []Step { + alice_power := uint(30) + bob_power := uint(29) + carol_power := uint(20) + david_power := uint(10) + eve_power := uint(7) + fred_power := uint(4) + + return []Step{ + { + Action: StartChainAction{ + Chain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: alice_power * 1000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: bob_power * 1000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: carol_power * 1000000, Allocation: 10000000000}, + {Id: ValidatorID("david"), Stake: david_power * 1000000, Allocation: 10000000000}, + {Id: ValidatorID("eve"), Stake: eve_power * 1000000, Allocation: 10000000000}, + {Id: ValidatorID("fred"), Stake: fred_power * 1000000, Allocation: 10000000000}, + }, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): alice_power, + ValidatorID("bob"): bob_power, + ValidatorID("carol"): carol_power, + ValidatorID("david"): david_power, + ValidatorID("eve"): 0, // max provider consensus validators is 4, so eve and fred are at 0 power + ValidatorID("fred"): 0, + }, + StakedTokens: &map[ValidatorID]uint{ + ValidatorID("alice"): alice_power * 1000000, + ValidatorID("bob"): bob_power * 1000000, + ValidatorID("carol"): carol_power * 1000000, + ValidatorID("david"): david_power * 1000000, + ValidatorID("eve"): eve_power * 1000000, + ValidatorID("fred"): fred_power * 1000000, + }, + }, + }, + }, + { + Action: SubmitConsumerAdditionProposalAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + Deposit: 10000001, + ConsumerChain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + TopN: 100, + AllowInactiveVals: false, + }, + 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: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_VOTING_PERIOD)), + }, + }, + }, + }, + }, + { + Action: VoteGovProposalAction{ + Chain: ChainID("provi"), + From: []ValidatorID{ValidatorID("alice"), ValidatorID("bob"), ValidatorID("carol"), ValidatorID("david"), ValidatorID("eve")}, + Vote: []string{"yes", "yes", "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: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_PASSED)), + }, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {"consu"}, + ValidatorID("bob"): {"consu"}, + ValidatorID("carol"): {"consu"}, + ValidatorID("david"): {"consu"}, + ValidatorID("eve"): {}, + ValidatorID("fred"): {}, + }, + }, + }, + }, + { + Action: StartConsumerChainAction{ + ConsumerChain: ChainID("consu"), + ProviderChain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: alice_power * 1000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: bob_power * 1000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: carol_power * 1000000, Allocation: 10000000000}, + {Id: ValidatorID("david"), Stake: david_power * 1000000, Allocation: 10000000000}, + {Id: ValidatorID("eve"), Stake: eve_power * 1000000, Allocation: 10000000000}, + {Id: ValidatorID("fred"), Stake: fred_power * 1000000, 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"): alice_power, + ValidatorID("bob"): bob_power, + ValidatorID("carol"): carol_power, + ValidatorID("david"): david_power, + ValidatorID("eve"): 0, + ValidatorID("fred"): 0, + }, + }, + }, + }, + } +} diff --git a/tests/e2e/steps_compatibility.go b/tests/e2e/steps_compatibility.go index dfcf14cd58..ab6e9dbc47 100644 --- a/tests/e2e/steps_compatibility.go +++ b/tests/e2e/steps_compatibility.go @@ -8,6 +8,7 @@ import ( gov "github.com/cosmos/cosmos-sdk/x/gov/types/v1" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) func compstepStartProviderChain() []Step { @@ -98,7 +99,7 @@ func compstepsStartConsumerChain(consumerName string, proposalIndex, chainIndex 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", + ExpectedError: providertypes.ErrConsumerKeyInUse.Error(), }, State: State{ ChainID(consumerName): ChainState{ diff --git a/tests/e2e/steps_democracy.go b/tests/e2e/steps_democracy.go index 82e1d237af..ef9c3787f0 100644 --- a/tests/e2e/steps_democracy.go +++ b/tests/e2e/steps_democracy.go @@ -152,6 +152,7 @@ func stepsDemocracy(consumerName string, expectRegisteredRewardDistribution bool }, { Action: SubmitChangeRewardDenomsProposalAction{ + Chain: ChainID("provi"), Denom: consumerRewardDenom, Deposit: 10000001, From: ValidatorID("bob"), diff --git a/tests/e2e/steps_inactive_vals.go b/tests/e2e/steps_inactive_vals.go new file mode 100644 index 0000000000..64957cbfb9 --- /dev/null +++ b/tests/e2e/steps_inactive_vals.go @@ -0,0 +1,975 @@ +package main + +import ( + "strconv" + + gov "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" +) + +// stepsInactiveValidatorsOnConsumer tests situations where validators that are *not* in the active set on the +// provider chain validate on the consumer chain. +// The provider chain is set to have at most *2* validators active in consensus, and there are 3 validators in total. +// high-level, this test does: +// - start the provider chain +// - start a consumer chain +// - check that non-consensus validators do not get slashed for downtime on the provider; and that they don't get rewards +// - check that active validators *do* get slashed for downtime on the provider, and don't get rewards while they are down +// - check that non-consensus validators *do* get jailed for consumer downtime on the provider +// - check that non-consensus validators *become* consensus validators when they have enough power +func stepsInactiveProviderValidators() []Step { + s := concatSteps( + []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"): 0, // max consensus validators is 2, so alice should not be in power + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + StakedTokens: &map[ValidatorID]uint{ + ValidatorID("alice"): 100000000, + ValidatorID("bob"): 200000000, + ValidatorID("carol"): 300000000, + }, + Rewards: &Rewards{ + IsNativeDenom: true, // check for rewards in the provider denom + IsIncrementalReward: true, // we need to get incremental rewards + // if we would look at total rewards, alice would trivially also get rewards, + // because she gets rewards in the first block due to being in the genesis + IsRewarded: map[ValidatorID]bool{ + ValidatorID("alice"): false, + ValidatorID("bob"): true, + ValidatorID("carol"): true, + }, + }, + }, + }, + }, + }, + setupOptInChain(), + []Step{ + // check that active-but-not-consensus validators do not get slashed for downtime + { + // alices provider node goes offline + Action: DowntimeSlashAction{ + Chain: ChainID("provi"), + Validator: ValidatorID("alice"), + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, // still 0 consensus power + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + StakedTokens: &map[ValidatorID]uint{ + ValidatorID("alice"): 100000000, // but alice does not get jailed or slashed + ValidatorID("bob"): 200000000, + ValidatorID("carol"): 300000000, + }, + }, + }, + }, + // give carol more power so that she has enough power to validate if bob goes down + { + Action: DelegateTokensAction{ + Chain: ChainID("provi"), + From: ValidatorID("carol"), + To: ValidatorID("carol"), + Amount: 700000000, // carol needs to have more than 2/3rds of power(alice) + power(carol) + power(bob) to run both chains alone, so we stake some more to her + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 200, + ValidatorID("carol"): 1000, + }, + StakedTokens: &map[ValidatorID]uint{ + ValidatorID("alice"): 100000000, + ValidatorID("bob"): 200000000, + ValidatorID("carol"): 1000000000, + }, + // check that bob and carol get rewards, but alice does not + Rewards: &Rewards{ + IsNativeDenom: true, // check for rewards in the provider denom + IsIncrementalReward: true, // check rewards since block 1 + IsRewarded: map[ValidatorID]bool{ + ValidatorID("alice"): false, + ValidatorID("bob"): true, + ValidatorID("carol"): true, + }, + }, + }, + }, + }, + // bob goes offline + { + Action: DowntimeSlashAction{ + Chain: ChainID("provi"), + Validator: ValidatorID("bob"), + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, // alice gets into the active set + ValidatorID("bob"): 0, // bob is jailed + ValidatorID("carol"): 1000, + }, + StakedTokens: &map[ValidatorID]uint{ + ValidatorID("alice"): 100000000, + ValidatorID("bob"): 198000000, // 1% slash + ValidatorID("carol"): 1000000000, + }, + }, + }, + }, + { + // relay packets so that the consumer gets up to date with the provider + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("provi"): ChainState{ + Rewards: &Rewards{ + IsNativeDenom: true, // check for rewards in the provider denom + IsIncrementalReward: true, // check rewards for currently produced blocks only + IsRewarded: map[ValidatorID]bool{ + ValidatorID("alice"): true, // alice is participating right now, so gets rewards + ValidatorID("bob"): false, // bob does not get rewards since he is not participating in consensus + ValidatorID("carol"): true, + }, + }, + }, + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 0, + ValidatorID("carol"): 1000, + }, + }, + }, + }, + // unjail bob + { + Action: UnjailValidatorAction{ + Provider: ChainID("provi"), + Validator: ValidatorID("bob"), + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, // alice is back out because only 2 validators can be active in consensus + ValidatorID("bob"): 198, // bob was slashed 1% + ValidatorID("carol"): 1000, + }, + // check that between two blocks now, alice does not get rewarded with the native denom + Rewards: &Rewards{ + IsNativeDenom: true, // check for rewards in the provider denom + IsIncrementalReward: true, // check rewards for currently produced blocks only + IsRewarded: map[ValidatorID]bool{ + ValidatorID("alice"): false, + ValidatorID("bob"): true, + ValidatorID("carol"): true, + }, + }, + }, + // bob is still at 0 power on the consumer chain + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, + ValidatorID("bob"): 0, + ValidatorID("carol"): 1000, + }, + }, + }, + }, + // relay packets so that the consumer gets up to date with the provider + { + 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"): 198, + ValidatorID("carol"): 1000, + }, + }, + }, + }, + // alice goes offline on the consumer chain + { + Action: DowntimeSlashAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("alice"), + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, // power not affected yet + ValidatorID("bob"): 198, + ValidatorID("carol"): 1000, + }, + }, + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, // alice is not consensus-active anyways, since we allow two vals at maximum + ValidatorID("bob"): 198, + ValidatorID("carol"): 1000, + }, + }, + }, + }, + // relay the packets so that the provider chain knows about alice's downtime + { + Action: RelayPacketsAction{ + ChainA: ChainID("consu"), + ChainB: ChainID("provi"), + Port: "consumer", + Channel: 0, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, // alice is still not in the active set, and should now be jailed too. + // we cannot test directly whether alice is jailed, but we will test this below + ValidatorID("bob"): 198, + ValidatorID("carol"): 1000, + }, + }, + }, + }, + // we need to double-check that alice is actually jailed, so we get bob jailed, too, which usually would mean alice gets into power + { + Action: DowntimeSlashAction{ + Chain: ChainID("provi"), + Validator: ValidatorID("bob"), + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, // alice is jailed + ValidatorID("bob"): 0, // bob is jailed + ValidatorID("carol"): 1000, + }, + }, + }, + }, + // relay the packets so that the consumer chain is in sync again + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, // alice is jailed + ValidatorID("bob"): 0, // bob is jailed + ValidatorID("carol"): 1000, + }, + }, + }, + }, + // unjail alice + { + Action: UnjailValidatorAction{ + Provider: ChainID("provi"), + Validator: ValidatorID("alice"), + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + // alice was not slashed because consumer downtime just jails without slashing tokens + ValidatorID("alice"): 100, // alice is back as an active consensus validator. + ValidatorID("bob"): 0, // bob is still jailed + ValidatorID("carol"): 1000, + }, + }, + }, + }, + // unjail bob + { + Action: UnjailValidatorAction{ + Provider: ChainID("provi"), + Validator: ValidatorID("bob"), + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, // alice is back out because only 2 validators can be active in consensus + ValidatorID("bob"): 196, // bob is back as an active consensus validator and lost 2 more power due to the second downtime + ValidatorID("carol"): 1000, + }, + }, + }, + }, + // relay the packets so that the consumer chain is in sync again + { + Action: RelayPacketsAction{ + ChainA: ChainID("provi"), + ChainB: ChainID("consu"), + Port: "provider", + Channel: 0, + }, + State: State{ + ChainID("consu"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 100, // both alice and bob are validating the consumer + ValidatorID("bob"): 196, + ValidatorID("carol"): 1000, + }, + }, + }, + }, + }, + ) + + return s +} + +// Precondition: The provider chain is running. +// Postcondition: A consumer chain with Top N = 0 is running, including an up-and-running IBC connection to the provider. +// "alice", "bob", "carol" have opted in and are validating. +func setupOptInChain() []Step { + return concatSteps([]Step{ + { + Action: SubmitConsumerAdditionProposalAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + Deposit: 10000001, + ConsumerChain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + TopN: 0, + AllowInactiveVals: true, + }, + 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: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_VOTING_PERIOD)), + }, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, + ValidatorID("carol"): {}, + }, + }, + }, + }, + }, + stepsOptInValidators("alice", "bob", "carol"), + []Step{ + { + 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: strconv.Itoa(int(gov.ProposalStatus_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, + 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{}, + }, + }, + ) +} + +func stepsOptInValidators(validators ...ValidatorID) []Step { + s := make([]Step, 0) + for _, val := range validators { + // Οpt in all validators + s = append(s, Step{ + Action: OptInAction{ + Chain: ChainID("consu"), + Validator: val, + }, + State: State{ + ChainID("provi"): ChainState{}, + }, + }, + ) + } + return s +} + +// stepsInactiveProviderValidatorsGovernance validates that inactive validators +// are not included in the calculation of the quorum for governance proposals. +// It checks that when the quorum is met *among active validators*, +// the proposal can pass, even though the quorum would not be met if inactive validators +// would be counted. +func stepsInactiveProviderValidatorsGovernance() []Step { + s := concatSteps( + []Step{ + { + Action: StartChainAction{ + Chain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 290000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 290000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, // max consensus validators is 1, so alice and bob should not be in power + ValidatorID("bob"): 0, + ValidatorID("carol"): 300, + }, + StakedTokens: &map[ValidatorID]uint{ + ValidatorID("alice"): 290000000, + ValidatorID("bob"): 290000000, + ValidatorID("carol"): 300000000, + }, + }, + }, + }, + }, + []Step{ + // create a governance proposal + { + 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: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_VOTING_PERIOD)), + }, + }, + }, + }, + }, + // vote for it with carol + { + Action: VoteGovProposalAction{ + Chain: ChainID("provi"), + From: []ValidatorID{ValidatorID("carol")}, + Vote: []string{"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}, + // the proposal should have passed because carol voted for it. + // carol alone is enough to pass the quorum, because stake of the other validators is not counted + Status: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_PASSED)), + }, + }, + }, + }, + }, + }, + ) + + return s +} + +// stepsInactiveProviderValidatorsGovernanceBasecase is a sanity check to go along with +// stepsInactiveProviderValidatorsGovernance. It tests that with all validators being active, +// the proposal does not pass if it does not meet the quorum among validators. +func stepsInactiveProviderValidatorsGovernanceBasecase() []Step { + s := concatSteps( + []Step{ + { + Action: StartChainAction{ + Chain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 290000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 290000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 290, + ValidatorID("bob"): 290, + ValidatorID("carol"): 300, + }, + StakedTokens: &map[ValidatorID]uint{ + ValidatorID("alice"): 290000000, + ValidatorID("bob"): 290000000, + ValidatorID("carol"): 300000000, + }, + }, + }, + }, + }, + []Step{ + // create a governance proposal + { + 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: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_VOTING_PERIOD)), + }, + }, + }, + }, + }, + // vote for it with carol + { + Action: VoteGovProposalAction{ + Chain: ChainID("provi"), + From: []ValidatorID{ValidatorID("carol")}, + Vote: []string{"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}, + // the proposal should *not* have passed because only carol voted for it, + // and carol is not enough to pass the quorum + Status: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_REJECTED)), + }, + }, + }, + }, + }, + }, + ) + + return s +} + +// stepsMinStake validates that a validator with less stake than the specified minStake parameter +// cannot validate the consumer chain. +func stepsMinStake() []Step { + return concatSteps( + []Step{ + { + Action: StartChainAction{ + Chain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 290000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 290000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 300000000, Allocation: 10000000000}, + }, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 290, + ValidatorID("bob"): 290, + ValidatorID("carol"): 300, + }, + StakedTokens: &map[ValidatorID]uint{ + ValidatorID("alice"): 290000000, + ValidatorID("bob"): 290000000, + ValidatorID("carol"): 300000000, + }, + }, + }, + }, + // create a governance proposal + { + Action: SubmitConsumerAdditionProposalAction{ + Chain: ChainID("provi"), + From: ValidatorID("alice"), + Deposit: 10000001, + ConsumerChain: ChainID("consu"), + SpawnTime: 0, + InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 1}, + TopN: 0, + MinStake: 300000000, + }, + 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: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_VOTING_PERIOD)), + }, + }, + }, + }, + }, + }, + stepsOptInValidators("alice", "bob", "carol"), + []Step{ + { + 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: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_PASSED)), + }, + }, + }, + }, + }, + { + // we start all the validators, but due to the min stake, only carol can validate + 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"): 0, + ValidatorID("bob"): 0, + ValidatorID("carol"): 300, // due to min stake of 300000000, only carol can validate + }, + }, + }, + }, + }, + ) +} + +// This test case validates that inactive validators are not included when computing +// the top N. +func stepsInactiveValsWithTopN() []Step { + return []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"): 0, // max consensus validators is 2, so alice should not be in power + ValidatorID("bob"): 200, + ValidatorID("carol"): 300, + }, + StakedTokens: &map[ValidatorID]uint{ + ValidatorID("alice"): 100000000, + ValidatorID("bob"): 200000000, + ValidatorID("carol"): 300000000, + }, + Rewards: &Rewards{ + IsNativeDenom: true, // check for rewards in the provider denom + IsIncrementalReward: true, // we need to get incremental rewards + // if we would look at total rewards, alice would trivially also get rewards, + // because she gets rewards in the first block due to being in the genesis + IsRewarded: map[ValidatorID]bool{ + ValidatorID("alice"): false, + ValidatorID("bob"): true, + ValidatorID("carol"): true, + }, + }, + }, + }, + }, + { + 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: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_VOTING_PERIOD)), + }, + }, + }, + }, + }, + { + 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: strconv.Itoa(int(gov.ProposalStatus_PROPOSAL_STATUS_PASSED)), + }, + }, + HasToValidate: &map[ValidatorID][]ChainID{ + ValidatorID("alice"): {}, + ValidatorID("bob"): {}, // bob doesn't have to validate because he is not in the top N + ValidatorID("carol"): {"consu"}, + }, + }, + }, + }, + { + 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"): 0, // alice and bob are not in the top N, so aren't in the validator set + ValidatorID("bob"): 0, + ValidatorID("carol"): 300, + }, + }, + }, + }, + } +} + +// stepsInactiveValsMint tests the minting of tokens with inactive validators +// It checks that inactive validators are not counted when computing whether the +// inflation rate should go up or down. +func stepsInactiveValsMint() []Step { + // total supply is 30000000000, bonded goal ratio makes it so we want 30000000 tokens bonded + return []Step{ + { + Action: StartChainAction{ + Chain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 27000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 28000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 29000000, Allocation: 10000000000}, + }, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 0, + ValidatorID("carol"): 29, // other validators are not in power since only 1 can be active + }, + InflationRateChange: intPtr(1), // inflation rate goes up because less than the goal is bonded, since only carol is active + }, + }, + }, + { + Action: DelegateTokensAction{ + Chain: ChainID("provi"), + From: ValidatorID("carol"), + To: ValidatorID("carol"), + Amount: 50000000, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 0, + ValidatorID("bob"): 0, + ValidatorID("carol"): 79, + }, + InflationRateChange: intPtr(-1), // inflation rate goes down now, because carol has more bonded than the goal + }, + }, + }, + } +} + +// stepsMintBasecase tests the minting of tokens without inactive validators. +// This is done as a sanity check to complement stepsInactiveValsMint. +func stepsMintBasecase() []Step { + // total supply is 30000000000, bonded goal ratio makes it so we want 30000000 tokens bonded + return []Step{ + { + Action: StartChainAction{ + Chain: ChainID("provi"), + Validators: []StartChainValidator{ + {Id: ValidatorID("alice"), Stake: 27000000, Allocation: 10000000000}, + {Id: ValidatorID("bob"), Stake: 28000000, Allocation: 10000000000}, + {Id: ValidatorID("carol"), Stake: 29000000, Allocation: 10000000000}, + }, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 27, + ValidatorID("bob"): 28, + ValidatorID("carol"): 29, + }, + InflationRateChange: intPtr(-1), // inflation rate goes down because more than the goal is bonded + }, + }, + }, + { + Action: DelegateTokensAction{ + Chain: ChainID("provi"), + From: ValidatorID("carol"), + To: ValidatorID("carol"), + Amount: 50000000, + }, + State: State{ + ChainID("provi"): ChainState{ + ValPowers: &map[ValidatorID]uint{ + ValidatorID("alice"): 27, + ValidatorID("bob"): 28, + ValidatorID("carol"): 79, + }, + InflationRateChange: intPtr(-1), // inflation rate *still* goes down + }, + }, + }, + } +} diff --git a/tests/e2e/steps_multi_consumer_downtime.go b/tests/e2e/steps_multi_consumer_downtime.go index 7e73197b8f..02f070a612 100644 --- a/tests/e2e/steps_multi_consumer_downtime.go +++ b/tests/e2e/steps_multi_consumer_downtime.go @@ -1,6 +1,6 @@ package main -// stepsDowntime tests validator jailing and slashing. +// stepsMultiConsumerDowntimeFromConsumer tests validator jailing and slashing. // No slashing should occur for downtime slash initiated from the consumer chain // validators will simply be jailed in those cases // If an infraction is committed on the provider chain then the validator will be slashed diff --git a/tests/e2e/steps_partial_set_security.go b/tests/e2e/steps_partial_set_security.go index 98316f45bb..80eff1aedd 100644 --- a/tests/e2e/steps_partial_set_security.go +++ b/tests/e2e/steps_partial_set_security.go @@ -29,6 +29,16 @@ func stepsOptInChain() []Step { }, }, }, + { + Action: SetConsumerCommissionRateAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("bob"), + CommissionRate: 0.123, + ExpectError: true, + ExpectedError: "unknown consumer chain", + }, + State: State{}, + }, { Action: SubmitConsumerAdditionProposalAction{ Chain: ChainID("provi"), @@ -89,6 +99,31 @@ func stepsOptInChain() []Step { ValidatorID("carol"): {}, }, }, + ChainID("consu"): ChainState{ + // no consumer commission rates were set and hence we get + // the default (i.e., 0.1) commission rate the validators have on the provider + ConsumerCommissionRates: &map[ValidatorID]float64{ + ValidatorID("alice"): 0.1, + ValidatorID("bob"): 0.1, + ValidatorID("carol"): 0.1, + }, + }, + }, + }, + { + Action: SetConsumerCommissionRateAction{ + Chain: ChainID("consu"), + Validator: ValidatorID("bob"), + CommissionRate: 0.123, + }, + State: State{ + ChainID("consu"): ChainState{ + ConsumerCommissionRates: &map[ValidatorID]float64{ + ValidatorID("alice"): 0.1, + ValidatorID("bob"): 0.123, + ValidatorID("carol"): 0.1, + }, + }, }, }, { diff --git a/tests/e2e/steps_sovereign_changeover.go b/tests/e2e/steps_sovereign_changeover.go index 500120bbf0..ac6b7025cc 100644 --- a/tests/e2e/steps_sovereign_changeover.go +++ b/tests/e2e/steps_sovereign_changeover.go @@ -197,11 +197,12 @@ func stepRunSovereignChain() []Step { func stepsUpgradeChain() []Step { return []Step{ { - Action: LegacyUpgradeProposalAction{ + Action: UpgradeProposalAction{ ChainID: ChainID("sover"), UpgradeTitle: "sovereign-changeover", Proposer: ValidatorID("alice"), UpgradeHeight: 110, + Expedited: false, }, State: State{ ChainID("sover"): ChainState{ diff --git a/tests/e2e/test_driver.go b/tests/e2e/test_driver.go index 9c59411ac7..f65ddfe6ab 100644 --- a/tests/e2e/test_driver.go +++ b/tests/e2e/test_driver.go @@ -83,7 +83,7 @@ func (td *DefaultDriver) getTargetDriver(chainID ChainID) Chain { } icsVersion := td.getIcsVersion(chainID) switch icsVersion { - case "v4": + case "v3", "v4": if td.verbose { fmt.Println("Using 'v4' driver for chain ", chainID) } @@ -101,7 +101,7 @@ func (td *DefaultDriver) getTargetDriver(chainID ChainID) Chain { target: td.target, } if td.verbose { - fmt.Println("Using default driver ", icsVersion, " for chain ", chainID) + fmt.Println("Using default driver for version", icsVersion, " for chain ", chainID) } } @@ -129,8 +129,8 @@ func (td *DefaultDriver) runAction(action interface{}) error { target.startChain(action, td.verbose) case StartSovereignChainAction: target.startSovereignChain(action, td.verbose) - case LegacyUpgradeProposalAction: - target.submitLegacyUpgradeProposal(action, td.verbose) + case UpgradeProposalAction: + target.submitUpgradeProposal(action, td.verbose) case WaitUntilBlockAction: target.waitUntilBlockOnChain(action) case ChangeoverChainAction: @@ -140,13 +140,31 @@ func (td *DefaultDriver) runAction(action interface{}) error { case SubmitTextProposalAction: target.submitTextProposal(action, td.verbose) case SubmitConsumerAdditionProposalAction: - target.submitConsumerAdditionProposal(action, td.verbose) + target = td.getTargetDriver(action.Chain) + version := target.testConfig.providerVersion + if semver.IsValid(version) && semver.Compare(semver.Major(version), "v5") < 0 { + target.submitConsumerAdditionLegacyProposal(action, td.verbose) + } else { + target.submitConsumerAdditionProposal(action, td.verbose) + } case SubmitConsumerRemovalProposalAction: - target.submitConsumerRemovalProposal(action, td.verbose) + version := target.testConfig.providerVersion + target = td.getTargetDriver(action.Chain) + if semver.IsValid(version) && semver.Compare(semver.Major(version), "v5") < 0 { + target.submitConsumerRemovalLegacyProposal(action, td.verbose) + } else { + target.submitConsumerRemovalProposal(action, td.verbose) + } case SubmitEnableTransfersProposalAction: target.submitEnableTransfersProposalAction(action, td.verbose) case SubmitConsumerModificationProposalAction: - target.submitConsumerModificationProposal(action, td.verbose) + target = td.getTargetDriver(action.Chain) + version := target.testConfig.providerVersion + if semver.IsValid(version) && semver.Compare(semver.Major(version), "v5") < 0 { + target.submitConsumerModificationLegacyProposal(action, td.verbose) + } else { + target.submitConsumerModificationProposal(action, td.verbose) + } case VoteGovProposalAction: target.voteGovProposal(action, td.verbose) case StartConsumerChainAction: @@ -202,11 +220,19 @@ func (td *DefaultDriver) runAction(action interface{}) error { case StartConsumerEvidenceDetectorAction: target.startConsumerEvidenceDetector(action, td.verbose) case SubmitChangeRewardDenomsProposalAction: - target.submitChangeRewardDenomsProposal(action, td.verbose) + target = td.getTargetDriver(action.Chain) + version := target.testConfig.providerVersion + if semver.IsValid(version) && semver.Compare(semver.Major(version), "v5") < 0 { + target.submitChangeRewardDenomsLegacyProposal(action, td.verbose) + } else { + target.submitChangeRewardDenomsProposal(action, td.verbose) + } case OptInAction: target.optIn(action, td.target, td.verbose) case OptOutAction: target.optOut(action, td.target, td.verbose) + case SetConsumerCommissionRateAction: + target.setConsumerCommissionRate(action, td.target, td.verbose) default: log.Fatalf("unknown action in testRun %s: %#v", td.testCfg.name, action) } diff --git a/tests/e2e/testlib/types.go b/tests/e2e/testlib/types.go index 135f07a6a8..bd7996b988 100644 --- a/tests/e2e/testlib/types.go +++ b/tests/e2e/testlib/types.go @@ -37,6 +37,8 @@ type ChainCommands interface { GetValPower(chain ChainID, validator ValidatorID) uint GetValStakedTokens(chain ChainID, validatorAddress string) uint GetQueryNodeIP(chain ChainID) string + GetInflationRate(chain ChainID) float64 + GetConsumerCommissionRate(chain ChainID, validator ValidatorID) float64 } // TODO: replace ExecutionTarget with new TargetDriver interface @@ -153,7 +155,6 @@ type ProposalAndType struct { RawProposal json.RawMessage Type string } - type ChainState struct { ValBalances *map[ValidatorID]uint Proposals *map[uint]Proposal @@ -170,6 +171,8 @@ type ChainState struct { RegisteredConsumerRewardDenoms *[]string ClientsFrozenHeights *map[string]clienttypes.Height HasToValidate *map[ValidatorID][]ChainID // only relevant to provider chain + InflationRateChange *int // whether the inflation rate between two blocks changes negatively (any negative number), is equal (0), or changes positively (any positive number) + ConsumerCommissionRates *map[ValidatorID]float64 } // custom marshal and unmarshal functions for the chainstate that convert proposals to/from the auxiliary type with type info diff --git a/tests/e2e/testlib/utils.go b/tests/e2e/testlib/utils.go index 8b68c25947..08f410269f 100644 --- a/tests/e2e/testlib/utils.go +++ b/tests/e2e/testlib/utils.go @@ -7,9 +7,7 @@ import ( "os/exec" ) -var verbose *bool //TODO: remove hack - -func ExecuteCommandWithVerbosity(cmd *exec.Cmd, cmdName string, verbose bool) { +func ExecuteCommand(cmd *exec.Cmd, cmdName string, verbose bool) { if verbose { fmt.Println(cmdName+" cmd:", cmd.String()) } @@ -36,8 +34,3 @@ func ExecuteCommandWithVerbosity(cmd *exec.Cmd, cmdName string, verbose bool) { log.Fatal(err) } } - -// Executes a command with verbosity specified by CLI flag -func ExecuteCommand(cmd *exec.Cmd, cmdName string) { - ExecuteCommandWithVerbosity(cmd, cmdName, *verbose) -} diff --git a/tests/e2e/testnet-scripts/sovereign-genesis.json b/tests/e2e/testnet-scripts/sovereign-genesis.json index ba95b2fbce..bf7c881b1d 100644 --- a/tests/e2e/testnet-scripts/sovereign-genesis.json +++ b/tests/e2e/testnet-scripts/sovereign-genesis.json @@ -192,9 +192,20 @@ "threshold": "0.500000000000000000", "veto_threshold": "0.334000000000000000", "min_initial_deposit_ratio": "0.000000000000000000", + "proposal_cancel_ratio": "0.500000000000000000", + "proposal_cancel_dest": "", + "expedited_voting_period": "10s", + "expedited_threshold": "0.667000000000000000", + "expedited_min_deposit": [ + { + "denom": "stake", + "amount": "50000000" + } + ], "burn_vote_quorum": false, "burn_proposal_deposit_prevote": false, - "burn_vote_veto": true + "burn_vote_veto": true, + "min_deposit_ratio": "0.010000000000000000" } }, "ibc": { @@ -228,7 +239,16 @@ "send_sequences": [], "recv_sequences": [], "ack_sequences": [], - "next_channel_sequence": "0" + "next_channel_sequence": "0", + "params": { + "upgrade_timeout": { + "height": { + "revision_number": "0", + "revision_height": "0" + }, + "timestamp": "600000000000" + } + } } }, "mint": { diff --git a/tests/e2e/testnet-scripts/start-changeover.sh b/tests/e2e/testnet-scripts/start-changeover.sh index f41b6ce626..7d1bd7c2a9 100644 --- a/tests/e2e/testnet-scripts/start-changeover.sh +++ b/tests/e2e/testnet-scripts/start-changeover.sh @@ -179,7 +179,7 @@ QUERY_NODE_SUFFIX=$(echo "$VALIDATORS" | jq -r ".[0].ip_suffix") echo "NODE SUFFIX: $QUERY_NODE_SUFFIX" # poll for chain start set +e -until $BIN query block query block --type=height 0 --node "tcp://$CHAIN_IP_PREFIX.$QUERY_NODE_SUFFIX:26658" | grep -q -v '{"block_id":{"hash":"","parts":{"total":0,"hash":""}},"block":null}'; do sleep 0.3 ; done +until $BIN query block --type=height 0 --node "tcp://$CHAIN_IP_PREFIX.$QUERY_NODE_SUFFIX:26658" | grep -q -v '{"block_id":{"hash":"","parts":{"total":0,"hash":""}},"block":null}'; do sleep 0.3 ; done set -e echo "done!!!!!!!!" diff --git a/tests/e2e/v4/state.go b/tests/e2e/v4/state.go index 70ca8afe7c..594004bc24 100644 --- a/tests/e2e/v4/state.go +++ b/tests/e2e/v4/state.go @@ -236,7 +236,7 @@ func (tr Commands) GetBalance(chain ChainID, validator ValidatorID) uint { // interchain-securityd query gov proposals func (tr Commands) GetProposal(chain ChainID, proposal uint) Proposal { - var noProposalRegex = regexp.MustCompile(`doesn't exist: key not found`) + noProposalRegex := regexp.MustCompile(`doesn't exist: key not found`) binaryName := tr.ChainConfigs[chain].BinaryName bz, err := tr.Target.ExecCommand(binaryName, @@ -411,6 +411,7 @@ func (tr Commands) GetConsumerChains(chain ChainID) map[ChainID]bool { return chains } + func (tr Commands) GetConsumerAddress(consumerChain ChainID, validator ValidatorID) string { binaryName := tr.ChainConfigs[ChainID("provi")].BinaryName cmd := tr.Target.ExecCommand(binaryName, @@ -538,7 +539,7 @@ func (tr Commands) curlJsonRPCRequest(method, params, address string) { cmd := tr.Target.ExecCommand("bash", "-c", fmt.Sprintf(cmd_template, method, params, address)) verbosity := false - e2e.ExecuteCommandWithVerbosity(cmd, "curlJsonRPCRequest", verbosity) + e2e.ExecuteCommand(cmd, "curlJsonRPCRequest", verbosity) } // GetClientFrozenHeight returns the frozen height for a client with the given client ID @@ -650,9 +651,19 @@ func (tr Commands) GetIBCTransferParams(chain ChainID) IBCTransferParams { } func (tr Commands) GetHasToValidate(validator ValidatorID) []ChainID { - panic("''GetHasToValidate' is not implemented in this version") + panic("'GetHasToValidate' is not implemented in this version") +} + +func (tr Commands) GetConsumerCommissionRate(chain ChainID, validator ValidatorID) float64 { + panic("'GetConsumerCommissionRate' is not implemented in this version") } func uintPtr(i uint) *uint { return &i } + +func (tr Commands) GetInflationRate( + chain ChainID, +) float64 { + panic("'GetInflationRate' is not implemented in this version") +} diff --git a/tests/integration/README.md b/tests/integration/README.md index 294ae7384a..5ebbd8d97b 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -5,7 +5,6 @@ Integration tests are categorized into files as follows: - `setup.go` - setup for the integration tests - `common.go` - helper functions -- `channel_init.go` - integration tests for the _Channel Initialization_ sub-protocol - `valset_update.go` - integration tests for the _Validator Set Update_ sub-protocol - `unbonding.go` - integration tests for the _Completion of Unbonding Operations_ - `slashing.go` - integration tests for the _Consumer Initiated Slashing_ sub-protocol @@ -17,6 +16,7 @@ Integration tests are categorized into files as follows: - `changeover.go` - integration tests for testing reuse of existing transfer channels - `double_vote.go` - integration tests for testing the handling of double voting - `misbehavior.go` - integration tests for testing the handling of misbehaviors +- `partial_set_security_test.go` - integration tests for testing the partial set security - `expired_client.go` - integration tests for testing expired clients - `key_assignment.go` - integration tests for testing key assignment - `instance_test.go` - ties the integration test structure into golang's standard test mechanism, with appropriate definitions for concrete app types and setup callback diff --git a/tests/integration/channel_init.go b/tests/integration/channel_init.go deleted file mode 100644 index bfc51dffb7..0000000000 --- a/tests/integration/channel_init.go +++ /dev/null @@ -1,102 +0,0 @@ -package integration - -// TestInitTimeout tests that the init timeout is respected. -// @Long Description@ -// The test sets up a provider with a configured init timeout period. -// It then creates a connection between the provider and consumer chains, -// and then performs a handshake (stopping at various stages of the process to simulate a timeout). -// It then increments the time by the init timeout period and checks that the chain was removed if the timeout was reached, -// or that the chain was not removed if the handshake was indeed completed before the timeout. -func (suite *CCVTestSuite) TestInitTimeout() { - testCases := []struct { - name string - handshake func() - removed bool - }{ - { - "init times out before INIT", func() {}, true, - }, - { - "init times out before TRY", func() { - // send ChanOpenInit - err := suite.path.EndpointA.ChanOpenInit() - suite.Require().NoError(err) - }, true, - }, - { - "init times out before ACK", func() { - // send ChanOpenInit - err := suite.path.EndpointA.ChanOpenInit() - suite.Require().NoError(err) - // send ChanOpenTry - err = suite.path.EndpointB.ChanOpenTry() - suite.Require().NoError(err) - }, true, - }, - { - "init times out before CONFIRM", func() { - // send ChanOpenInit - err := suite.path.EndpointA.ChanOpenInit() - suite.Require().NoError(err) - // send ChanOpenTry - err = suite.path.EndpointB.ChanOpenTry() - suite.Require().NoError(err) - // send ChanOpenAck - err = suite.path.EndpointA.ChanOpenAck() - suite.Require().NoError(err) - }, true, - }, - { - "init completes before timeout", func() { - // send ChanOpenInit - err := suite.path.EndpointA.ChanOpenInit() - suite.Require().NoError(err) - // send ChanOpenTry - err = suite.path.EndpointB.ChanOpenTry() - suite.Require().NoError(err) - // send ChanOpenAck - err = suite.path.EndpointA.ChanOpenAck() - suite.Require().NoError(err) - // send ChanOpenConfirm - err = suite.path.EndpointB.ChanOpenConfirm() - suite.Require().NoError(err) - }, false, - }, - } - - for i, tc := range testCases { - providerKeeper := suite.providerApp.GetProviderKeeper() - initTimeout := providerKeeper.GetParams(suite.providerCtx()).InitTimeoutPeriod - chainID := suite.consumerChain.ChainID - - // check that the init timeout timestamp is set - _, found := providerKeeper.GetInitTimeoutTimestamp(suite.providerCtx(), chainID) - suite.Require().True(found, "cannot find init timeout timestamp; test: %s", tc.name) - - // create connection - suite.coordinator.CreateConnections(suite.path) - - // channel opening handshake - tc.handshake() - - // call NextBlock - suite.providerChain.NextBlock() - - // increment time - incrementTime(suite, initTimeout) - - // check whether the chain was removed - _, found = providerKeeper.GetConsumerClientId(suite.providerCtx(), chainID) - suite.Require().Equal(!tc.removed, found, "unexpected outcome; test: %s", tc.name) - - if tc.removed { - // check if the chain was properly removed - suite.checkConsumerChainIsRemoved(chainID, false) - } - - if i+1 < len(testCases) { - // reset suite to reset provider client - suite.SetupTest() - } - } -} diff --git a/tests/integration/common.go b/tests/integration/common.go index cc8a0e86a5..46ef499d01 100644 --- a/tests/integration/common.go +++ b/tests/integration/common.go @@ -301,6 +301,7 @@ func relayAllCommittedPackets( // // Note that it is expected for the provider unbonding period // to be one day larger than the consumer unbonding period. +// TODO (mpoke) get rid of consumer unbonding period func incrementTimeByUnbondingPeriod(s *CCVTestSuite, chainType ChainType) { // Get unboding periods providerUnbondingPeriod, err := s.providerApp.GetTestStakingKeeper().UnbondingTime(s.providerCtx()) @@ -315,7 +316,7 @@ func incrementTimeByUnbondingPeriod(s *CCVTestSuite, chainType ChainType) { incrementTime(s, jumpPeriod) } -func checkStakingUnbondingOps(s *CCVTestSuite, id uint64, found, onHold bool, msgAndArgs ...interface{}) { +func checkStakingUnbondingOps(s *CCVTestSuite, id uint64, found bool, msgAndArgs ...interface{}) { stakingUnbondingOp, wasFound := getStakingUnbondingDelegationEntry(s.providerCtx(), s.providerApp.GetTestStakingKeeper(), id) s.Require().Equal( found, @@ -323,53 +324,15 @@ func checkStakingUnbondingOps(s *CCVTestSuite, id uint64, found, onHold bool, ms fmt.Sprintf("checkStakingUnbondingOps failed - getStakingUnbondingDelegationEntry; %s", msgAndArgs...), ) if wasFound { - s.Require().True( - onHold == (0 < stakingUnbondingOp.UnbondingOnHoldRefCount), - fmt.Sprintf("checkStakingUnbondingOps failed - onHold; %s", msgAndArgs...), - ) - } -} - -func checkCCVUnbondingOp(s *CCVTestSuite, providerCtx sdk.Context, chainID string, valUpdateID uint64, found bool, msgAndArgs ...interface{}) { - entries := s.providerApp.GetProviderKeeper().GetUnbondingOpsFromIndex(providerCtx, chainID, valUpdateID) - if found { - s.Require().NotEmpty(entries, fmt.Sprintf("checkCCVUnbondingOp failed - should not be empty; %s", msgAndArgs...)) - s.Require().Greater( - len(entries), - 0, - fmt.Sprintf("checkCCVUnbondingOp failed - no unbonding ops found; %s", msgAndArgs...), - ) - s.Require().Greater( - len(entries[0].UnbondingConsumerChains), - 0, - fmt.Sprintf("checkCCVUnbondingOp failed - unbonding op with no consumer chains; %s", msgAndArgs...), - ) + // make sure UnbondingOnHoldRefCount remains zero s.Require().Equal( - "testchain2", - entries[0].UnbondingConsumerChains[0], - fmt.Sprintf("checkCCVUnbondingOp failed - unbonding op with unexpected consumer chain; %s", msgAndArgs...), + int64(0), + stakingUnbondingOp.UnbondingOnHoldRefCount, + fmt.Sprintf("checkStakingUnbondingOps failed - UnbondingOnHoldRefCount; %s", msgAndArgs...), ) } } -// Checks that an expected amount of redelegations exist for a delegator -// via the staking keeper, then returns those redelegations. -func checkRedelegations(s *CCVTestSuite, delAddr sdk.AccAddress, - expect uint16, -) []stakingtypes.Redelegation { - redelegations, err := s.providerApp.GetTestStakingKeeper().GetRedelegations(s.providerCtx(), delAddr, 2) - s.Require().NoError(err) - s.Require().Len(redelegations, int(expect)) - return redelegations -} - -// Checks that a redelegation entry has a completion time equal to an expected time -func checkRedelegationEntryCompletionTime( - s *CCVTestSuite, entry stakingtypes.RedelegationEntry, expectedCompletion time.Time, -) { - s.Require().Equal(expectedCompletion, entry.CompletionTime) -} - func getStakingUnbondingDelegationEntry(ctx sdk.Context, k testutil.TestStakingKeeper, id uint64) (stakingUnbondingOp stakingtypes.UnbondingDelegationEntry, found bool) { stakingUbd, err := k.GetUnbondingDelegationByUnbondingID(ctx, id) if err != nil { diff --git a/tests/integration/distribution.go b/tests/integration/distribution.go index 4693233182..3fa6ac5164 100644 --- a/tests/integration/distribution.go +++ b/tests/integration/distribution.go @@ -133,7 +133,10 @@ func (s *CCVTestSuite) TestRewardsDistribution() { // 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) { + vals, err := providerKeeper.GetConsumerValSet(ctx, s.consumerChain.ChainID) + s.Require().NoError(err) + + for _, v := range vals { val, err := s.providerApp.GetTestStakingKeeper().GetValidatorByConsAddr(ctx, sdk.ConsAddress(v.ProviderConsAddr)) s.Require().NoError(err) valAddr, err := sdk.ValAddressFromBech32(val.GetOperator()) @@ -555,6 +558,9 @@ func (s *CCVTestSuite) TestIBCTransferMiddleware() { getIBCDenom func(string, string) string ) + // set up an arbitrary address that is not the consumer rewards pool address + notConsumerRewardsPoolAddr := s.providerChain.SenderAccount.GetAddress().String() + testCases := []struct { name string setup func(sdk.Context, *providerkeeper.Keeper, icstestingutils.TestBankKeeper) @@ -581,7 +587,7 @@ func (s *CCVTestSuite) TestIBCTransferMiddleware() { { "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 + data.Receiver = notConsumerRewardsPoolAddr packet.Data = data.GetBytes() }, false, @@ -926,10 +932,12 @@ func (s *CCVTestSuite) TestAllocateTokensToConsumerValidators() { ctx.BlockHeight()) // change the consumer valset - consuVals := providerKeeper.GetConsumerValSet(ctx, chainID) + consuVals, err := providerKeeper.GetConsumerValSet(ctx, chainID) + s.Require().NoError(err) providerKeeper.DeleteConsumerValSet(ctx, chainID) providerKeeper.SetConsumerValSet(ctx, chainID, consuVals[0:tc.consuValLen]) - consuVals = providerKeeper.GetConsumerValSet(ctx, chainID) + consuVals, err = providerKeeper.GetConsumerValSet(ctx, chainID) + s.Require().NoError(err) // set the same consumer commission rate for all consumer validators for _, v := range consuVals { @@ -1034,7 +1042,8 @@ func (s *CCVTestSuite) TestAllocateTokensToConsumerValidatorsWithDifferentValida ctx = ctx.WithBlockHeight(providerKeeper.GetNumberOfEpochsToStartReceivingRewards(ctx)*providerKeeper.GetBlocksPerEpoch(ctx) + 1) // update the consumer validators - consuVals := providerKeeper.GetConsumerValSet(ctx, chainID) + consuVals, err := providerKeeper.GetConsumerValSet(ctx, chainID) + s.Require().NoError(err) // first 2 validators were consumer validators since block height 1 and hence get rewards consuVals[0].JoinHeight = 1 consuVals[1].JoinHeight = 1 @@ -1046,7 +1055,8 @@ func (s *CCVTestSuite) TestAllocateTokensToConsumerValidatorsWithDifferentValida providerKeeper.DeleteConsumerValSet(ctx, chainID) providerKeeper.SetConsumerValSet(ctx, chainID, consuVals) - consuVals = providerKeeper.GetConsumerValSet(ctx, chainID) + consuVals, err = providerKeeper.GetConsumerValSet(ctx, chainID) + s.Require().NoError(err) // set the same consumer commission rate for all consumer validators for _, v := range consuVals { diff --git a/tests/integration/expired_client.go b/tests/integration/expired_client.go index 2d164145bf..409338493e 100644 --- a/tests/integration/expired_client.go +++ b/tests/integration/expired_client.go @@ -210,7 +210,7 @@ func expireClient(s *CCVTestSuite, clientTo ChainType) { checkClientExpired(s, clientTo, true) } -// checkClientIsExpired checks whether the client to `clientTo` is expired +// checkClientExpired checks whether the client to `clientTo` is expired func checkClientExpired(s *CCVTestSuite, clientTo ChainType, expectedExpired bool) { var hostEndpoint *ibctesting.Endpoint var hostChain *ibctesting.TestChain diff --git a/tests/integration/partial_set_security_test.go b/tests/integration/partial_set_security_test.go new file mode 100644 index 0000000000..7f015b5778 --- /dev/null +++ b/tests/integration/partial_set_security_test.go @@ -0,0 +1,188 @@ +package integration + +import ( + "slices" + "sort" + "testing" + + "cosmossdk.io/math" + ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" + "github.com/stretchr/testify/require" + + icstestingutils "github.com/cosmos/interchain-security/v5/testutil/ibc_testing" + + appConsumer "github.com/cosmos/interchain-security/v5/app/consumer" + appProvider "github.com/cosmos/interchain-security/v5/app/provider" +) + +// we need a stake multiplier because tokens do not directly correspond to voting power +// this is needed because 1000000 tokens = 1 voting power, so lower multipliers +// will be verbose and harder to read because small token numbers +// won't correspond to at least one voting power +const stakeMultiplier = 1000000 + +// TestMinStake tests the min stake parameter. +// @Long Description@ +// It starts a provider and single consumer chain, +// sets the initial powers according to the input, and then +// sets the min stake parameter according to the test case. +// Finally, it checks that the validator set on the consumer chain is as expected +// according to the min stake parameter. +func TestMinStake(t *testing.T) { + testCases := []struct { + name string + stakedTokens []int64 + minStake uint64 + expectedConsuValSet []int64 + }{ + { + name: "disabled min stake", + stakedTokens: []int64{ + 1 * stakeMultiplier, + 2 * stakeMultiplier, + 3 * stakeMultiplier, + 4 * stakeMultiplier, + }, + minStake: 0, + expectedConsuValSet: []int64{ + 1 * stakeMultiplier, + 2 * stakeMultiplier, + 3 * stakeMultiplier, + 4 * stakeMultiplier, + }, + }, + { + name: "stake multiplier - standard case", + stakedTokens: []int64{ + 1 * stakeMultiplier, + 2 * stakeMultiplier, + 3 * stakeMultiplier, + 4 * stakeMultiplier, + }, + minStake: 3 * stakeMultiplier, + expectedConsuValSet: []int64{ + 3 * stakeMultiplier, + 4 * stakeMultiplier, + }, + }, + { + name: "check min stake with multiple equal stakes", + stakedTokens: []int64{ + 1 * stakeMultiplier, + 2 * stakeMultiplier, + 2 * stakeMultiplier, + 2 * stakeMultiplier, + }, + minStake: 2 * stakeMultiplier, + expectedConsuValSet: []int64{ + 2 * stakeMultiplier, + 2 * stakeMultiplier, + 2 * stakeMultiplier, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + s := NewCCVTestSuite[*appProvider.App, *appConsumer.App]( + // Pass in ibctesting.AppIniters for provider and consumer. + icstestingutils.ProviderAppIniter, icstestingutils.ConsumerAppIniter, []string{}) + s.SetT(t) + s.SetupTest() + + providerKeeper := s.providerApp.GetProviderKeeper() + s.SetupCCVChannel(s.path) + + // set validator powers + vals, err := providerKeeper.GetLastBondedValidators(s.providerChain.GetContext()) + s.Require().NoError(err) + + delegatorAccount := s.providerChain.SenderAccounts[0] + + for i, val := range vals { + power := tc.stakedTokens[i] + valAddr, err := providerKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator()) + s.Require().NoError(err) + undelegate(s, delegatorAccount.SenderAccount.GetAddress(), valAddr, math.LegacyOneDec()) + + // set validator power + delegateByIdx(s, delegatorAccount.SenderAccount.GetAddress(), math.NewInt(power), i) + } + + // end the epoch to apply the updates + s.nextEpoch() + + // Relay 1 VSC packet from provider to consumer + relayAllCommittedPackets(s, s.providerChain, s.path, ccv.ProviderPortID, s.path.EndpointB.ChannelID, 1) + + // end the block on the consumer to apply the updates + s.consumerChain.NextBlock() + + // get the last bonded validators + lastVals, err := providerKeeper.GetLastBondedValidators(s.providerChain.GetContext()) + s.Require().NoError(err) + + // Assuming tc.stakedTokens is defined somewhere in your test case + // Create a copy of the tc.stakedTokens slice + sortedTokens := make([]int64, len(tc.stakedTokens)) + copy(sortedTokens, tc.stakedTokens) + + // Sort the copied slice in descending order + sort.Slice(sortedTokens, func(i, j int) bool { + return sortedTokens[i] > sortedTokens[j] + }) + + for i, val := range lastVals { + // check that the initial state was set correctly + require.Equal(s.T(), math.NewInt(sortedTokens[i]), val.Tokens) + } + + // check the validator set on the consumer chain is the original one + consuValSet := s.consumerChain.LastHeader.ValidatorSet + s.Require().Equal(len(consuValSet.Validators), 4) + + // get just the powers of the consu val set + consuValPowers := make([]int64, len(consuValSet.Validators)) + for i, consuVal := range consuValSet.Validators { + // voting power corresponds to staked tokens at a 1:stake_multiplier ratio + consuValPowers[i] = consuVal.VotingPower * stakeMultiplier + } + + s.Require().ElementsMatch(consuValPowers, tc.stakedTokens) + + // adjust parameters + + // set the minStake according to the test case + providerKeeper.SetMinStake(s.providerChain.GetContext(), s.consumerChain.ChainID, tc.minStake) + + // delegate and undelegate to trigger a vscupdate + + // first delegate + delegateAndUndelegate(s, delegatorAccount.SenderAccount.GetAddress(), math.NewInt(1*stakeMultiplier), 1) + + // end the epoch to apply the updates + s.nextEpoch() + + if slices.Equal(tc.stakedTokens, tc.expectedConsuValSet) { + // don't expect to relay a packet + relayAllCommittedPackets(s, s.providerChain, s.path, ccv.ProviderPortID, s.path.EndpointB.ChannelID, 0) + } else { + // Relay 1 VSC packet from provider to consumer + relayAllCommittedPackets(s, s.providerChain, s.path, ccv.ProviderPortID, s.path.EndpointB.ChannelID, 1) + } + + // end the block on the consumer to apply the updates + s.consumerChain.NextBlock() + + // construct the new val powers + newConsuValSet := s.consumerChain.LastHeader.ValidatorSet + newConsuValPowers := make([]int64, len(newConsuValSet.Validators)) + for i, consuVal := range newConsuValSet.Validators { + // voting power corresponds to staked tokens at a 1:stake_multiplier ratio + newConsuValPowers[i] = consuVal.VotingPower * stakeMultiplier + } + + // check that the new validator set is as expected + s.Require().ElementsMatch(newConsuValPowers, tc.expectedConsuValSet) + }) + } +} diff --git a/tests/integration/provider_gov_hooks.go b/tests/integration/provider_gov_hooks.go index 322f772be4..c0e786042d 100644 --- a/tests/integration/provider_gov_hooks.go +++ b/tests/integration/provider_gov_hooks.go @@ -26,15 +26,9 @@ func (s *CCVTestSuite) TestAfterPropSubmissionAndVotingPeriodEnded() { govKeeper := s.providerApp.GetTestGovKeeper() proposer := s.providerChain.SenderAccount - content := testkeeper.GetTestConsumerAdditionProp() - content.ChainId = "newchain-0" - legacyPropContent, err := v1.NewLegacyContent( - content, - authtypes.NewModuleAddress("gov").String(), - ) - s.Require().NoError(err) + addConsumerProp := testkeeper.GetTestMsgConsumerAddition() - proposal, err := v1.NewProposal([]sdk.Msg{legacyPropContent}, 1, time.Now(), time.Now().Add(1*time.Hour), "metadata", "title", "summary", proposer.GetAddress(), false) + proposal, err := v1.NewProposal([]sdk.Msg{&addConsumerProp}, 1, time.Now(), time.Now().Add(1*time.Hour), "metadata", "title", "summary", proposer.GetAddress(), false) s.Require().NoError(err) err = govKeeper.SetProposal(ctx, proposal) @@ -46,23 +40,22 @@ func (s *CCVTestSuite) TestAfterPropSubmissionAndVotingPeriodEnded() { proposalIdOnProvider, ok := providerKeeper.GetProposedConsumerChain(ctx, proposal.Id) s.Require().True(ok) s.Require().NotEmpty(proposalIdOnProvider) - s.Require().Equal(content.ChainId, proposalIdOnProvider) + s.Require().Equal(addConsumerProp.ChainId, proposalIdOnProvider) providerKeeper.Hooks().AfterProposalVotingPeriodEnded(ctx, proposal.Id) // verify that the proposal ID is deleted s.Require().Empty(providerKeeper.GetProposedConsumerChain(ctx, proposal.Id)) } -// TestGetConsumerAdditionLegacyPropFromProp manually calls the GetConsumerAdditionLegacyPropFromProp hook on +// TestGetConsumerAdditionFromProp manually calls the GetConsumerAdditionLegacyPropFromProp hook on // various types of proposals to test the behavior of the hook. // @Long Description@ -// The tes case created a provider chain, -// then submits a Proposal with various different types of content. +// The test case creates a provider chain, then submits a Proposal with various different types of content. // Then, it tries to get the ConsumerAdditionProposal from the proposal using the hook. // Test cases include a proposal with no messages; a proposal with a transfer message; a proposal with an unrelated legacy proposal; // a proposal with an invalid legacy proposal; and a proposal with a ConsumerAdditionProposal. // In the case of a valid ConsumerAdditionProposal, the test verifies that the proposal is found and returned by the hook. -func (s *CCVTestSuite) TestGetConsumerAdditionLegacyPropFromProp() { +func (s *CCVTestSuite) TestGetConsumerAdditionFromProp() { ctx := s.providerChain.GetContext() proposer := s.providerChain.SenderAccount @@ -73,13 +66,19 @@ func (s *CCVTestSuite) TestGetConsumerAdditionLegacyPropFromProp() { Amount: sdk.NewCoins(sdk.NewCoin("stake", math.OneInt())), } + // create a legacy proposal textProp, err := v1.NewLegacyContent( v1beta1.NewTextProposal("a title", "a legacy text prop"), authtypes.NewModuleAddress("gov").String(), ) s.Require().NoError(err) - addConsumerProp, err := v1.NewLegacyContent( + // create a valid consumer addition message + msgConsumerAddition := testkeeper.GetTestMsgConsumerAddition() + + // create a legacy consumer addition proposal content + // (not supported anymore) + addConsumerPropLegacy, err := v1.NewLegacyContent( testkeeper.GetTestConsumerAdditionProp(), authtypes.NewModuleAddress("gov").String(), ) @@ -98,7 +97,7 @@ func (s *CCVTestSuite) TestGetConsumerAdditionLegacyPropFromProp() { expPanic: false, }, { - name: "msgs in prop contain no legacy props", + name: "msgs in prop contain no consumer addition props", propMsg: dummyMsg, expectConsumerPropFound: false, expPanic: false, @@ -112,11 +111,17 @@ func (s *CCVTestSuite) TestGetConsumerAdditionLegacyPropFromProp() { name: "msgs contain an invalid legacy prop", propMsg: &v1.MsgExecLegacyContent{}, expectConsumerPropFound: false, - expPanic: true, + expPanic: false, + }, + { + name: "msg contains a prop of legacy ConsumerAdditionProposal type - hook should NOT create a new proposed chain", + propMsg: addConsumerPropLegacy, + expectConsumerPropFound: false, + expPanic: false, }, { - name: "msg contains a prop of ConsumerAdditionProposal type - hook should create a new proposed chain", - propMsg: addConsumerProp, + name: "msg contains a prop of MsgConsumerAddition type - hook should create a new proposed chain", + propMsg: &msgConsumerAddition, expectConsumerPropFound: true, expPanic: false, }, @@ -146,12 +151,12 @@ func (s *CCVTestSuite) TestGetConsumerAdditionLegacyPropFromProp() { if tc.expPanic { s.Require().Panics(func() { // this panics with a nil pointer dereference because the proposal is invalid and cannot be unmarshalled - providerKeeper.Hooks().GetConsumerAdditionLegacyPropFromProp(ctx, proposal.Id) + providerKeeper.Hooks().GetConsumerAdditionFromProp(ctx, proposal.Id) }) return } - savedProp, found := providerKeeper.Hooks().GetConsumerAdditionLegacyPropFromProp(ctx, proposal.Id) + savedProp, found := providerKeeper.Hooks().GetConsumerAdditionFromProp(ctx, proposal.Id) if tc.expectConsumerPropFound { s.Require().True(found) s.Require().NotEmpty(savedProp, savedProp) diff --git a/tests/integration/slashing.go b/tests/integration/slashing.go index f58a7ef725..0ee9ff949e 100644 --- a/tests/integration/slashing.go +++ b/tests/integration/slashing.go @@ -115,14 +115,6 @@ func (s *CCVTestSuite) TestRelayAndApplyDowntimePacket() { // this call was added starging cosmos-sdk v0.50.x s.nextEpoch() - // VSC packets should have been sent from provider during block N to each consumer - expectedSentValsetUpdateId := valsetUpdateIdN - for _, bundle := range s.consumerBundles { - _, found := providerKeeper.GetVscSendTimestamp(s.providerCtx(), - bundle.Chain.ChainID, expectedSentValsetUpdateId) - s.Require().True(found) - } - s.nextEpoch() // Confirm the valset update Id was incremented twice on provider, @@ -133,6 +125,8 @@ func (s *CCVTestSuite) TestRelayAndApplyDowntimePacket() { // check that the validator was removed from the provider validator set by N + 2 s.Require().Len(s.providerChain.Vals.Validators, validatorsPerChain-1) + // VSC packets should have been sent from provider during block N to each consumer + expectedSentValsetUpdateId := valsetUpdateIdN for _, bundle := range s.consumerBundles { // Relay VSC packets from provider to each consumer relayAllCommittedPackets(s, s.providerChain, bundle.Path, @@ -454,7 +448,7 @@ func (suite *CCVTestSuite) TestOnRecvSlashPacketErrors() { suite.Require().NoError(err, "no error expected") suite.Require().Equal(ccv.SlashPacketHandledResult, ackResult, "expected successful ack") - providerKeeper.SetConsumerValidator(ctx, firstBundle.Chain.ChainID, providertypes.ConsumerValidator{ + providerKeeper.SetConsumerValidator(ctx, firstBundle.Chain.ChainID, providertypes.ConsensusValidator{ ProviderConsAddr: validAddress, }) diff --git a/tests/integration/stop_consumer.go b/tests/integration/stop_consumer.go index d8aa7c1bb5..71d6a6dc12 100644 --- a/tests/integration/stop_consumer.go +++ b/tests/integration/stop_consumer.go @@ -139,25 +139,12 @@ func (s *CCVTestSuite) TestStopConsumerOnChannelClosed() { func (s *CCVTestSuite) checkConsumerChainIsRemoved(chainID string, checkChannel bool) { channelID := s.path.EndpointB.ChannelID providerKeeper := s.providerApp.GetProviderKeeper() - providerStakingKeeper := s.providerApp.GetTestStakingKeeper() if checkChannel { // check channel's state is closed s.Require().Equal(channeltypes.CLOSED, s.path.EndpointB.GetChannel().State) } - // check UnbondingOps were deleted and undelegation entries aren't onHold - for _, unbondingOpsIndex := range providerKeeper.GetAllUnbondingOpIndexes(s.providerCtx(), chainID) { - _, found := providerKeeper.GetUnbondingOpIndex(s.providerCtx(), chainID, unbondingOpsIndex.VscId) - s.Require().False(found) - for _, ubdID := range unbondingOpsIndex.UnbondingOpIds { - _, found = providerKeeper.GetUnbondingOp(s.providerCtx(), unbondingOpsIndex.UnbondingOpIds[ubdID]) - s.Require().False(found) - ubd, _ := providerStakingKeeper.GetUnbondingDelegationByUnbondingID(s.providerCtx(), unbondingOpsIndex.UnbondingOpIds[ubdID]) - s.Require().Zero(ubd.Entries[ubdID].UnbondingOnHoldRefCount) - } - } - // verify consumer chain's states are removed _, found := providerKeeper.GetConsumerGenesis(s.providerCtx(), chainID) s.Require().False(found) diff --git a/tests/integration/unbonding.go b/tests/integration/unbonding.go index 6f027c3616..56fef0f125 100644 --- a/tests/integration/unbonding.go +++ b/tests/integration/unbonding.go @@ -1,529 +1,43 @@ package integration import ( - "time" - "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/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 -// the unbonding period elapses on both the consumer and provider, without -// VSC packets timing out. -func (s *CCVTestSuite) TestUndelegationNormalOperation() { - unbondConsumer := func(expectedPackets int) { - // relay 1 VSC packet from provider to consumer - relayAllCommittedPackets(s, s.providerChain, s.path, ccv.ProviderPortID, s.path.EndpointB.ChannelID, expectedPackets) - // increment time so that the unbonding period ends on the consumer - incrementTimeByUnbondingPeriod(s, Consumer) - // relay 1 VSCMatured packet from consumer to provider - relayAllCommittedPackets(s, s.consumerChain, s.path, ccv.ConsumerPortID, s.path.EndpointA.ChannelID, expectedPackets) - } - - testCases := []struct { - name string - shareDiv int64 - unbond func(expBalance, balance math.Int) - }{ - { - "provider unbonding period elapses first", 2, func(expBalance, balance math.Int) { - // increment time so that the unbonding period ends on the provider - incrementTimeByUnbondingPeriod(s, Provider) - - // check that onHold is true - checkStakingUnbondingOps(s, 1, true, true, "unbonding should be on hold") - - // check that the unbonding is not complete - s.Require().Equal(expBalance, balance, "unexpected balance after provider unbonding") - - // undelegation complete on consumer - unbondConsumer(1) - }, - }, - { - "consumer unbonding period elapses first", 2, func(expBalance, balance math.Int) { - // undelegation complete on consumer - unbondConsumer(1) - - // check that onHold is false - checkStakingUnbondingOps(s, 1, true, false, "unbonding should be not be on hold") - - // check that the unbonding is not complete - s.Require().Equal(expBalance, balance, "unexpected balance after consumer unbonding") - - // increment time so that the unbonding period ends on the provider - incrementTimeByUnbondingPeriod(s, Provider) - }, - }, - { - "no valset changes", 1, func(expBalance, balance math.Int) { - // undelegation complete on consumer - unbondConsumer(1) - - // check that onHold is false - checkStakingUnbondingOps(s, 1, true, false, "unbonding should be not be on hold") - - // check that the unbonding is not complete - s.Require().Equal(expBalance, balance, "unexpected balance after consumer unbonding") - - // increment time so that the unbonding period ends on the provider - incrementTimeByUnbondingPeriod(s, Provider) - }, - }, - } - - for i, tc := range testCases { - providerKeeper := s.providerApp.GetProviderKeeper() - consumerKeeper := s.consumerApp.GetConsumerKeeper() - stakingKeeper := s.providerApp.GetTestStakingKeeper() - - s.SetupCCVChannel(s.path) - - // set VSC timeout period to not trigger the removal of the consumer chain - providerUnbondingPeriod, err := stakingKeeper.UnbondingTime(s.providerCtx()) - s.Require().NoError(err) - consumerUnbondingPeriod := consumerKeeper.GetUnbondingPeriod(s.consumerCtx()) - providerKeeper.SetVscTimeoutPeriod(s.providerCtx(), providerUnbondingPeriod+consumerUnbondingPeriod+24*time.Hour) - - // delegate bondAmt and undelegate tc.shareDiv of it - bondAmt := math.NewInt(10000000) - delAddr := s.providerChain.SenderAccount.GetAddress() - initBalance, valsetUpdateID := delegateAndUndelegate(s, delAddr, bondAmt, tc.shareDiv) - // - check that staking unbonding op was created and onHold is true - checkStakingUnbondingOps(s, 1, true, true, "test: "+tc.name) - // - check that CCV unbonding op was created - checkCCVUnbondingOp(s, s.providerCtx(), s.consumerChain.ChainID, valsetUpdateID, true, "test: "+tc.name) - - // call NextBlock on the provider (which increments the height) - s.nextEpoch() - - // unbond both on provider and consumer and check that - // the balance remains unchanged in between - tc.unbond(initBalance.Sub(bondAmt), getBalance(s, s.providerCtx(), delAddr)) - - // check that the unbonding operation completed - // - check that ccv unbonding op has been deleted - checkCCVUnbondingOp(s, s.providerCtx(), s.consumerChain.ChainID, valsetUpdateID, false, "test: "+tc.name) - // - check that staking unbonding op has been deleted - checkStakingUnbondingOps(s, valsetUpdateID, false, false, "test: "+tc.name) - // - check that necessary delegated coins have been returned - unbondAmt := bondAmt.Sub(bondAmt.Quo(math.NewInt(tc.shareDiv))) - s.Require().Equal( - initBalance.Sub(unbondAmt), - getBalance(s, s.providerCtx(), delAddr), - "unexpected initial balance after unbonding; test: %s", tc.name, - ) - - if i+1 < len(testCases) { - // reset suite to reset provider client - s.SetupTest() - } - } -} - -// TestUndelegationVscTimeout tests that an undelegation -// completes after vscTimeoutPeriod even if it does not -// reach maturity on the consumer chain. In this case, -// the consumer chain is removed. -func (s *CCVTestSuite) TestUndelegationVscTimeout() { - providerKeeper := s.providerApp.GetProviderKeeper() - +// TestUndelegationCompletion tests that undelegations complete after +// the unbonding period elapses on the provider, regardless of the consumer's state +// Long Description: +// It sets up a CCV channel and performs an initial delegation of tokens followed by a partial undelegation +// (undelegating 1/4 of the tokens). Then it verifies that the staking unbonding operation is created as expected. Block height is then incremented +// on the provider. After this period elapses, the test checks that the unbonding operation has been completed. Finally, it verifies +// that the token balances are correctly updated, ensuring that the expected amount of tokens has been returned to the account. +func (s *CCVTestSuite) TestUndelegationCompletion() { s.SetupCCVChannel(s.path) - // set VSC timeout period to trigger the removal of the consumer chain - vscTimeout := providerKeeper.GetVscTimeoutPeriod(s.providerCtx()) - - // delegate bondAmt and undelegate 1/2 of it + // delegate bondAmt and undelegate 1/4 of it bondAmt := math.NewInt(10000000) delAddr := s.providerChain.SenderAccount.GetAddress() - initBalance, valsetUpdateID := delegateAndUndelegate(s, delAddr, bondAmt, 2) - // - check that staking unbonding op was created and onHold is true - checkStakingUnbondingOps(s, 1, true, true) - // - check that CCV unbonding op was created - checkCCVUnbondingOp(s, s.providerCtx(), s.consumerChain.ChainID, valsetUpdateID, true) + initBalance, valsetUpdateID := delegateAndUndelegate(s, delAddr, bondAmt, 4) + // - check that staking unbonding op was created + checkStakingUnbondingOps(s, 1, true) // call NextBlock on the provider (which increments the height) s.providerChain.NextBlock() - // increment time so that the unbonding period ends on the provider - incrementTimeByUnbondingPeriod(s, Provider) - - // check that onHold is true - checkStakingUnbondingOps(s, 1, true, true, "unbonding should be on hold") - - // check that the unbonding is not complete - s.Require().Equal( - initBalance.Sub(bondAmt), - getBalance(s, s.providerCtx(), delAddr), - "unexpected balance after provider unbonding") - - // increment time - incrementTime(s, vscTimeout) - - // check whether the chain was removed - chainID := s.consumerChain.ChainID - _, found := providerKeeper.GetConsumerClientId(s.providerCtx(), chainID) - s.Require().Equal(false, found, "consumer chain was not removed") - - // check if the chain was properly removed - s.checkConsumerChainIsRemoved(chainID, true) + // unbond on provider + stakingKeeper := s.providerApp.GetTestStakingKeeper() + unbondingPeriod, err := stakingKeeper.UnbondingTime(s.providerCtx()) + s.Require().NoError(err) + incrementTime(s, unbondingPeriod) // check that the unbonding operation completed - // - check that ccv unbonding op has been deleted - checkCCVUnbondingOp(s, s.providerCtx(), s.consumerChain.ChainID, valsetUpdateID, false) - // - check that staking unbonding op has been deleted - checkStakingUnbondingOps(s, valsetUpdateID, false, false) + checkStakingUnbondingOps(s, valsetUpdateID, false) // - check that necessary delegated coins have been returned - unbondAmt := bondAmt.Sub(bondAmt.Quo(math.NewInt(2))) + unbondAmt := bondAmt.Quo(math.NewInt(4)) + stillBondedAmt := bondAmt.Sub(unbondAmt) s.Require().Equal( - initBalance.Sub(unbondAmt), + initBalance.Sub(stillBondedAmt), getBalance(s, s.providerCtx(), delAddr), - "unexpected initial balance after VSC timeout", + "unexpected initial balance after unbonding; test: %s", ) } - -// TestUndelegationDuringInit checks that before the CCV channel is established -// - no undelegations can complete, even if the provider unbonding period elapses -// - all the VSC packets are stored in state as pending -// - if the channel handshake times out, then the undelegation completes -func (s *CCVTestSuite) TestUndelegationDuringInit() { - testCases := []struct { - name string - updateInitTimeoutTimestamp func(*providerkeeper.Keeper, time.Duration) - removed bool - }{ - { - "channel handshake completes after unbonding period", func(pk *providerkeeper.Keeper, pUnbondingPeriod time.Duration) { - // change the init timeout timestamp for this consumer chain - // to make sure the chain is not removed before the unbonding period elapses - ts := s.providerCtx().BlockTime().Add(pUnbondingPeriod + 24*time.Hour) - pk.SetInitTimeoutTimestamp(s.providerCtx(), s.consumerChain.ChainID, uint64(ts.UnixNano())) - }, false, - }, - { - "channel handshake times out before unbonding period", func(pk *providerkeeper.Keeper, pUnbondingPeriod time.Duration) { - // change the init timeout timestamp for this consumer chain - // to make sure the chain is removed before the unbonding period elapses - ts := s.providerCtx().BlockTime().Add(pUnbondingPeriod - 24*time.Hour) - pk.SetInitTimeoutTimestamp(s.providerCtx(), s.consumerChain.ChainID, uint64(ts.UnixNano())) - }, true, - }, - } - - for i, tc := range testCases { - providerKeeper := s.providerApp.GetProviderKeeper() - stakingKeeper := s.providerApp.GetTestStakingKeeper() - - // delegate bondAmt and undelegate 1/2 of it - bondAmt := math.NewInt(10000000) - delAddr := s.providerChain.SenderAccount.GetAddress() - initBalance, valsetUpdateID := delegateAndUndelegate(s, delAddr, bondAmt, 2) - // - check that staking unbonding op was created and onHold is true - checkStakingUnbondingOps(s, 1, true, true, "test: "+tc.name) - // - check that CCV unbonding op was created - checkCCVUnbondingOp(s, s.providerCtx(), s.consumerChain.ChainID, valsetUpdateID, true, "test: "+tc.name) - - // get provider unbonding period - providerUnbondingPeriod, err := stakingKeeper.UnbondingTime(s.providerCtx()) - s.Require().NoError(err) - // update init timeout timestamp - tc.updateInitTimeoutTimestamp(&providerKeeper, providerUnbondingPeriod) - - s.nextEpoch() - - // check that the VSC packet is stored in state as pending - pendingVSCs := providerKeeper.GetPendingVSCPackets(s.providerCtx(), s.consumerChain.ChainID) - s.Require().Lenf(pendingVSCs, 1, "no pending VSC packet found; test: %s", tc.name) - - // delegate again to create another VSC packet - delegate(s, delAddr, bondAmt) - - s.nextEpoch() - - // check that the VSC packet is stored in state as pending - pendingVSCs = providerKeeper.GetPendingVSCPackets(s.providerCtx(), s.consumerChain.ChainID) - s.Require().Lenf(pendingVSCs, 2, "only one pending VSC packet found; test: %s", tc.name) - - // increment time so that the unbonding period ends on the provider - incrementTimeByUnbondingPeriod(s, Provider) - - // check whether the unbonding op is still there and onHold is true - checkStakingUnbondingOps(s, 1, !tc.removed, true, "test: "+tc.name) - - if !tc.removed { - // check that unbonding has not yet completed, i.e., the initBalance - // is still lower by the bond amount, because it has been taken out of - // the delegator's account - s.Require().Equal( - initBalance.Sub(bondAmt).Sub(bondAmt), - getBalance(s, s.providerCtx(), delAddr), - "unexpected initial balance before unbonding; test: %s", tc.name, - ) - - // complete CCV channel setup - s.SetupCCVChannel(s.path) - s.nextEpoch() - - // relay VSC packets from provider to consumer - relayAllCommittedPackets(s, s.providerChain, s.path, ccv.ProviderPortID, s.path.EndpointB.ChannelID, 2) - - // increment time so that the unbonding period ends on the consumer - incrementTimeByUnbondingPeriod(s, Consumer) - - // relay VSCMatured packets from consumer to provider - relayAllCommittedPackets(s, s.consumerChain, s.path, ccv.ConsumerPortID, s.path.EndpointA.ChannelID, 2) - - // check that the unbonding operation completed - // - check that ccv unbonding op has been deleted - checkCCVUnbondingOp(s, s.providerCtx(), s.consumerChain.ChainID, valsetUpdateID, false, "test: "+tc.name) - // - check that staking unbonding op has been deleted - checkStakingUnbondingOps(s, valsetUpdateID, false, false, "test: "+tc.name) - // - check that one quarter the delegated coins have been returned - s.Require().Equal( - initBalance.Sub(bondAmt).Sub(bondAmt.Quo(math.NewInt(2))), - getBalance(s, s.providerCtx(), delAddr), - "unexpected initial balance after unbonding; test: %s", tc.name, - ) - } - - if i+1 < len(testCases) { - // reset suite to reset provider client - s.SetupTest() - } - } -} - -// Bond some tokens on provider -// Unbond them to create unbonding op -// Check unbonding ops on both sides -// Advance time so that provider's unbonding op completes -// Check that unbonding has completed in provider staking -func (s *CCVTestSuite) TestUnbondingNoConsumer() { - providerKeeper := s.providerApp.GetProviderKeeper() - providerStakingKeeper := s.providerApp.GetTestStakingKeeper() - - // remove all consumer chains, which were already started during setup - for chainID := range s.consumerBundles { - err := providerKeeper.StopConsumerChain(s.providerCtx(), chainID, true) - s.Require().NoError(err) - } - - // delegate bondAmt and undelegate 1/2 of it - bondAmt := math.NewInt(10000000) - delAddr := s.providerChain.SenderAccount.GetAddress() - initBalance, valsetUpdateID := delegateAndUndelegate(s, delAddr, bondAmt, 2) - // - check that staking unbonding op was created and onHold is FALSE - checkStakingUnbondingOps(s, 1, true, false) - // - check that CCV unbonding op was NOT created - checkCCVUnbondingOp(s, s.providerCtx(), s.consumerChain.ChainID, valsetUpdateID, false) - - // increment time so that the unbonding period ends on the provider; - // cannot use incrementTimeByUnbondingPeriod() since it tries - // to also update the provider's client on the consumer - providerUnbondingPeriod, err := providerStakingKeeper.UnbondingTime(s.providerCtx()) - s.Require().NoError(err) - s.coordinator.IncrementTimeBy(providerUnbondingPeriod + time.Hour) - - // call NextBlock on the provider (which increments the height) - s.providerChain.NextBlock() - - // check that the unbonding operation completed - // - check that staking unbonding op has been deleted - checkStakingUnbondingOps(s, valsetUpdateID, false, false) - // - check that half the coins have been returned - s.Require().True(getBalance(s, s.providerCtx(), delAddr).Equal(initBalance.Sub(bondAmt.Quo(math.NewInt(2))))) -} - -// TestRedelegationNoConsumer tests a redelegate transaction -// submitted on a provider chain with no consumers -func (s *CCVTestSuite) TestRedelegationNoConsumer() { - providerKeeper := s.providerApp.GetProviderKeeper() - stakingKeeper := s.providerApp.GetTestStakingKeeper() - - // stop the consumer chain, which was already started during setup - err := providerKeeper.StopConsumerChain(s.providerCtx(), s.consumerChain.ChainID, true) - s.Require().NoError(err) - - // Setup delegator, bond amount, and src/dst validators - bondAmt := math.NewInt(10000000) - delAddr := s.providerChain.SenderAccount.GetAddress() - _, srcVal := s.getValByIdx(0) - _, dstVal := s.getValByIdx(1) - - delegateAndRedelegate( - s, - delAddr, - srcVal, - dstVal, - bondAmt, - ) - - // 1 redelegation record should exist for original delegator - redelegations := checkRedelegations(s, delAddr, 1) - - // Check that the only entry has appropriate maturation time, the unbonding period from now - unbondingTime, err := stakingKeeper.UnbondingTime(s.providerCtx()) - s.Require().NoError(err) - checkRedelegationEntryCompletionTime( - s, - redelegations[0].Entries[0], - s.providerCtx().BlockTime().Add(unbondingTime), - ) - - // required before call to incrementTimeByUnbondingPeriod or else a panic - // occurs in ibc-go because trusted validators don't match last trusted. - s.providerChain.NextBlock() - - // Increment time so that the unbonding period passes on the provider - incrementTimeByUnbondingPeriod(s, Provider) - - // Call NextBlock on the provider (which increments the height) - s.providerChain.NextBlock() - - // No redelegation records should exist for original delegator anymore - checkRedelegations(s, delAddr, 0) -} - -// TestRedelegationWithConsumer tests a redelegate transaction submitted on a provider chain -// when the unbonding period elapses first on the provider chain -func (s *CCVTestSuite) TestRedelegationProviderFirst() { - s.SetupCCVChannel(s.path) - s.SetupTransferChannel() - - providerKeeper := s.providerApp.GetProviderKeeper() - consumerKeeper := s.consumerApp.GetConsumerKeeper() - stakingKeeper := s.providerApp.GetTestStakingKeeper() - - // set VSC timeout period to not trigger the removal of the consumer chain - providerUnbondingPeriod, err := stakingKeeper.UnbondingTime(s.providerCtx()) - s.Require().NoError(err) - consumerUnbondingPeriod := consumerKeeper.GetUnbondingPeriod(s.consumerCtx()) - providerKeeper.SetVscTimeoutPeriod(s.providerCtx(), providerUnbondingPeriod+consumerUnbondingPeriod+24*time.Hour) - - // Setup delegator, bond amount, and src/dst validators - bondAmt := math.NewInt(10000000) - delAddr := s.providerChain.SenderAccount.GetAddress() - _, srcVal := s.getValByIdx(0) - _, dstVal := s.getValByIdx(1) - - delegateAndRedelegate( - s, - delAddr, - srcVal, - dstVal, - bondAmt, - ) - - // 1 redelegation record should exist for original delegator - redelegations := checkRedelegations(s, delAddr, 1) - - // Check that the only entry has appropriate maturation time, the unbonding period from now - unbondingTime, err := stakingKeeper.UnbondingTime(s.providerCtx()) - s.Require().NoError(err) - checkRedelegationEntryCompletionTime( - s, - redelegations[0].Entries[0], - s.providerCtx().BlockTime().Add(unbondingTime), - ) - - // Save the current valset update ID - valsetUpdateID := providerKeeper.GetValidatorSetUpdateId(s.providerCtx()) - - // Check that CCV unbonding op was created from AfterUnbondingInitiated hook - checkCCVUnbondingOp(s, s.providerCtx(), s.consumerChain.ChainID, valsetUpdateID, true) - - // move forward by an epoch to be able to relay VSC packets - s.nextEpoch() - - // Relay 2 VSC packets from provider to consumer (original delegation, and redelegation) - relayAllCommittedPackets(s, s.providerChain, s.path, - ccv.ProviderPortID, s.path.EndpointB.ChannelID, 2) - - // Increment time so that the unbonding period ends on the provider - incrementTimeByUnbondingPeriod(s, Provider) - - // 1 redelegation record should still exist for original delegator on provider - checkRedelegations(s, delAddr, 1) - - // CCV unbonding op should also still exist - checkCCVUnbondingOp(s, s.providerCtx(), s.consumerChain.ChainID, valsetUpdateID, true) - - // Increment time so that the unbonding period ends on the consumer - incrementTimeByUnbondingPeriod(s, Consumer) - - // Relay 2 VSCMatured packets from consumer to provider (original delegation and redelegation) - relayAllCommittedPackets(s, s.consumerChain, - s.path, ccv.ConsumerPortID, s.path.EndpointA.ChannelID, 2) - - // - // Check that the redelegation operation has now completed on provider - // - - // Redelegation record should be deleted for original delegator - checkRedelegations(s, delAddr, 0) - - // 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() - pk := s.providerApp.GetProviderKeeper() - - getLastValsFn := func(ctx sdk.Context) []stakingtypes.Validator { - lastVals, err := pk.GetLastBondedValidators(s.providerCtx()) - s.Require().NoError(err) - return lastVals - } - - // get current staking params - p, err := sk.GetParams(s.providerCtx()) - s.Require().NoError(err) - - // get validators, which are all active at the moment - vals, err := sk.GetAllValidators(s.providerCtx()) - s.Require().NoError(err) - - s.Require().Equal(len(vals), len(getLastValsFn(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 := getLastValsFn(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(getLastValsFn(s.providerCtx()))) - - // update maximum validator to equal the number of bonded validators - p.MaxValidators = uint32(len(getLastValsFn(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() { getLastValsFn(s.providerCtx()) }) -} diff --git a/tests/mbt/driver/mbt_test.go b/tests/mbt/driver/mbt_test.go index 70fded614a..35ea2f4e1f 100644 --- a/tests/mbt/driver/mbt_test.go +++ b/tests/mbt/driver/mbt_test.go @@ -520,8 +520,6 @@ func RunItfTrace(t *testing.T, path string) { 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) // ensure that the jailed validators are the same in the model and the system, // and that the jail end times are the same, in particular @@ -784,30 +782,6 @@ func CompareValSet(modelValSet map[string]itf.Expr, systemValSet map[string]int6 return nil } -func CompareSentPacketsOnProvider(driver *Driver, currentModelState map[string]itf.Expr, timeOffset time.Time) { - 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, string(consumerChainID)) - - for i, modelVscSendTimestamp := range modelVscSendTimestamps { - actualTimeWithOffset := actualVscSendTimestamps[i].Unix() - timeOffset.Unix() - require.Equal( - driver.t, - modelVscSendTimestamp, - actualTimeWithOffset, - "Vsc send timestamps do not match for consumer %v", - consumerChainID, - ) - } - } -} - func CompareJailedValidators( driver *Driver, currentModelState map[string]itf.Expr, diff --git a/tests/mbt/driver/setup.go b/tests/mbt/driver/setup.go index acdae7554b..d9e658c030 100644 --- a/tests/mbt/driver/setup.go +++ b/tests/mbt/driver/setup.go @@ -438,8 +438,6 @@ func (s *Driver) setupProvider( providerChain := newChain(s.t, params, s.coordinator, icstestingutils.ProviderAppIniter, "provider", valSet, signers, nodes, valNames) s.coordinator.Chains["provider"] = providerChain - // set the VscTimeout - s.providerKeeper().SetVscTimeoutPeriod(s.ctx("provider"), params.VscTimeout) // set the CcvTimeoutPeriod providerParams := s.providerKeeper().GetParams(s.ctx("provider")) providerParams.CcvTimeoutPeriod = params.CcvTimeout[ChainId(providerChain.ChainID)] diff --git a/testutil/integration/debug_test.go b/testutil/integration/debug_test.go index d26246dcf5..dedd937eaa 100644 --- a/testutil/integration/debug_test.go +++ b/testutil/integration/debug_test.go @@ -49,14 +49,6 @@ func findAndCallMethod(t *testing.T, suite any, methodName string) { method.Func.Call([]reflect.Value{reflect.ValueOf(suite)}) } -// -// Channel init tests -// - -func TestInitTimeout(t *testing.T) { - runCCVTestByName(t, "TestInitTimeout") -} - // // Consumer democracy tests // @@ -201,28 +193,8 @@ func TestSlashAllValidators(t *testing.T) { // Unbonding tests // -func TestUndelegationNormalOperation(t *testing.T) { - runCCVTestByName(t, "TestUndelegationNormalOperation") -} - -func TestUndelegationVscTimeout(t *testing.T) { - runCCVTestByName(t, "TestUndelegationVscTimeout") -} - -func TestUndelegationDuringInit(t *testing.T) { - runCCVTestByName(t, "TestUndelegationDuringInit") -} - -func TestUnbondingNoConsumer(t *testing.T) { - runCCVTestByName(t, "TestUnbondingNoConsumer") -} - -func TestRedelegationNoConsumer(t *testing.T) { - runCCVTestByName(t, "TestRedelegationNoConsumer") -} - -func TestRedelegationProviderFirst(t *testing.T) { - runCCVTestByName(t, "TestRedelegationProviderFirst") +func TestUndelegationCompletion(t *testing.T) { + runCCVTestByName(t, "TestUndelegationCompletion") } // @@ -293,8 +265,8 @@ func TestAfterPropSubmissionAndVotingPeriodEnded(t *testing.T) { runCCVTestByName(t, "TestAfterPropSubmissionAndVotingPeriodEnded") } -func TestGetConsumerAdditionLegacyPropFromProp(t *testing.T) { - runCCVTestByName(t, "TestGetConsumerAdditionLegacyPropFromProp") +func TestGetConsumerAdditionFromProp(t *testing.T) { + runCCVTestByName(t, "TestGetConsumerAdditionFromProp") } func TestIBCTransferMiddleware(t *testing.T) { @@ -305,14 +277,6 @@ 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/keeper/expectations.go b/testutil/keeper/expectations.go index 9b57643167..3ceaf4fd2b 100644 --- a/testutil/keeper/expectations.go +++ b/testutil/keeper/expectations.go @@ -217,36 +217,18 @@ func GetMocksForSlashValidator( } } -// SetupMocksForLastBondedValidatorsExpectation sets up the expectation for the `IterateLastValidatorPowers` `MaxValidators`, and `GetValidator` methods of the `mockStakingKeeper` object. +// SetupMocksForLastBondedValidatorsExpectation sets up the expectation for the `GetBondedValidatorsByPower` and `MaxValidators` 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) error { - for i, val := range vals { - if stop := cb(sdk.ValAddress(val.OperatorAddress), powers[i]); stop { - break - } - } - return nil - }) +func SetupMocksForLastBondedValidatorsExpectation(mockStakingKeeper *MockStakingKeeper, maxValidators uint32, vals []stakingtypes.Validator, times int) { + validatorsCall := mockStakingKeeper.EXPECT().GetBondedValidatorsByPower(gomock.Any()).Return(vals, nil) maxValidatorsCall := mockStakingKeeper.EXPECT().MaxValidators(gomock.Any()).Return(maxValidators, nil) if times == -1 { - iteratorCall.AnyTimes() + validatorsCall.AnyTimes() maxValidatorsCall.AnyTimes() } else { - iteratorCall.Times(times) + validatorsCall.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, nil) - if times == -1 { - getValCall.AnyTimes() - } else { - getValCall.Times(times) - } - } } diff --git a/testutil/keeper/mocks.go b/testutil/keeper/mocks.go index 7beb0c0bcd..1a1cd971d5 100644 --- a/testutil/keeper/mocks.go +++ b/testutil/keeper/mocks.go @@ -9,6 +9,7 @@ import ( reflect "reflect" time "time" + address "cosmossdk.io/core/address" math "cosmossdk.io/math" types "cosmossdk.io/store/types" types0 "github.com/cometbft/cometbft/abci/types" @@ -62,6 +63,21 @@ func (mr *MockStakingKeeperMockRecorder) BondDenom(ctx interface{}) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BondDenom", reflect.TypeOf((*MockStakingKeeper)(nil).BondDenom), ctx) } +// BondedRatio mocks base method. +func (m *MockStakingKeeper) BondedRatio(ctx context.Context) (math.LegacyDec, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BondedRatio", ctx) + ret0, _ := ret[0].(math.LegacyDec) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BondedRatio indicates an expected call of BondedRatio. +func (mr *MockStakingKeeperMockRecorder) BondedRatio(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BondedRatio", reflect.TypeOf((*MockStakingKeeper)(nil).BondedRatio), ctx) +} + // Delegation mocks base method. func (m *MockStakingKeeper) Delegation(ctx context.Context, addr types1.AccAddress, valAddr types1.ValAddress) (types3.DelegationI, error) { m.ctrl.T.Helper() @@ -77,6 +93,21 @@ func (mr *MockStakingKeeperMockRecorder) Delegation(ctx, addr, valAddr interface return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delegation", reflect.TypeOf((*MockStakingKeeper)(nil).Delegation), ctx, addr, valAddr) } +// GetBondedValidatorsByPower mocks base method. +func (m *MockStakingKeeper) GetBondedValidatorsByPower(ctx context.Context) ([]types3.Validator, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBondedValidatorsByPower", ctx) + ret0, _ := ret[0].([]types3.Validator) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBondedValidatorsByPower indicates an expected call of GetBondedValidatorsByPower. +func (mr *MockStakingKeeperMockRecorder) GetBondedValidatorsByPower(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBondedValidatorsByPower", reflect.TypeOf((*MockStakingKeeper)(nil).GetBondedValidatorsByPower), ctx) +} + // GetLastTotalPower mocks base method. func (m *MockStakingKeeper) GetLastTotalPower(ctx context.Context) (math.Int, error) { m.ctrl.T.Helper() @@ -257,6 +288,34 @@ func (mr *MockStakingKeeperMockRecorder) IsValidatorJailed(ctx, addr interface{} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsValidatorJailed", reflect.TypeOf((*MockStakingKeeper)(nil).IsValidatorJailed), ctx, addr) } +// IterateBondedValidatorsByPower mocks base method. +func (m *MockStakingKeeper) IterateBondedValidatorsByPower(arg0 context.Context, arg1 func(int64, types3.ValidatorI) bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IterateBondedValidatorsByPower", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// IterateBondedValidatorsByPower indicates an expected call of IterateBondedValidatorsByPower. +func (mr *MockStakingKeeperMockRecorder) IterateBondedValidatorsByPower(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateBondedValidatorsByPower", reflect.TypeOf((*MockStakingKeeper)(nil).IterateBondedValidatorsByPower), arg0, arg1) +} + +// IterateDelegations mocks base method. +func (m *MockStakingKeeper) IterateDelegations(ctx context.Context, delegator types1.AccAddress, fn func(int64, types3.DelegationI) bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IterateDelegations", ctx, delegator, fn) + ret0, _ := ret[0].(error) + return ret0 +} + +// IterateDelegations indicates an expected call of IterateDelegations. +func (mr *MockStakingKeeperMockRecorder) IterateDelegations(ctx, delegator, fn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateDelegations", reflect.TypeOf((*MockStakingKeeper)(nil).IterateDelegations), ctx, delegator, fn) +} + // IterateLastValidatorPowers mocks base method. func (m *MockStakingKeeper) IterateLastValidatorPowers(ctx context.Context, cb func(types1.ValAddress, int64) bool) error { m.ctrl.T.Helper() @@ -417,6 +476,36 @@ func (mr *MockStakingKeeperMockRecorder) SlashWithInfractionReason(ctx, consAddr return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SlashWithInfractionReason", reflect.TypeOf((*MockStakingKeeper)(nil).SlashWithInfractionReason), ctx, consAddr, infractionHeight, power, slashFactor, infraction) } +// StakingTokenSupply mocks base method. +func (m *MockStakingKeeper) StakingTokenSupply(ctx context.Context) (math.Int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StakingTokenSupply", ctx) + ret0, _ := ret[0].(math.Int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StakingTokenSupply indicates an expected call of StakingTokenSupply. +func (mr *MockStakingKeeperMockRecorder) StakingTokenSupply(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StakingTokenSupply", reflect.TypeOf((*MockStakingKeeper)(nil).StakingTokenSupply), ctx) +} + +// TotalBondedTokens mocks base method. +func (m *MockStakingKeeper) TotalBondedTokens(ctx context.Context) (math.Int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TotalBondedTokens", ctx) + ret0, _ := ret[0].(math.Int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// TotalBondedTokens indicates an expected call of TotalBondedTokens. +func (mr *MockStakingKeeperMockRecorder) TotalBondedTokens(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TotalBondedTokens", reflect.TypeOf((*MockStakingKeeper)(nil).TotalBondedTokens), ctx) +} + // UnbondingCanComplete mocks base method. func (m *MockStakingKeeper) UnbondingCanComplete(ctx context.Context, id uint64) error { m.ctrl.T.Helper() @@ -475,6 +564,20 @@ func (mr *MockStakingKeeperMockRecorder) Validator(ctx, addr interface{}) *gomoc return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validator", reflect.TypeOf((*MockStakingKeeper)(nil).Validator), ctx, addr) } +// ValidatorAddressCodec mocks base method. +func (m *MockStakingKeeper) ValidatorAddressCodec() address.Codec { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidatorAddressCodec") + ret0, _ := ret[0].(address.Codec) + return ret0 +} + +// ValidatorAddressCodec indicates an expected call of ValidatorAddressCodec. +func (mr *MockStakingKeeperMockRecorder) ValidatorAddressCodec() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatorAddressCodec", reflect.TypeOf((*MockStakingKeeper)(nil).ValidatorAddressCodec)) +} + // ValidatorByConsAddr mocks base method. func (m *MockStakingKeeper) ValidatorByConsAddr(ctx context.Context, consAddr types1.ConsAddress) (types3.ValidatorI, error) { m.ctrl.T.Helper() diff --git a/testutil/keeper/unit_test_helpers.go b/testutil/keeper/unit_test_helpers.go index d139552371..52a76d93b0 100644 --- a/testutil/keeper/unit_test_helpers.go +++ b/testutil/keeper/unit_test_helpers.go @@ -2,6 +2,7 @@ package keeper import ( "crypto/rand" + "encoding/base64" "encoding/binary" "testing" "time" @@ -211,13 +212,6 @@ func GetNewSlashPacketData() types.SlashPacketData { } } -// Obtains vsc matured packet data with a newly generated key -func GetNewVSCMaturedPacketData() types.VSCMaturedPacketData { - b := make([]byte, 8) - _, _ = rand.Read(b) - return types.VSCMaturedPacketData{ValsetUpdateId: binary.BigEndian.Uint64(b)} -} - // SetupForStoppingConsumerChain registers expected mock calls and corresponding state setup // which assert that a consumer chain was properly setup to be later stopped from `StopConsumerChain`. // Note: This function only setups and tests that we correctly setup a consumer chain that we could later stop when @@ -228,7 +222,7 @@ func SetupForStoppingConsumerChain(t *testing.T, ctx sdk.Context, ) { t.Helper() - SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 1, []stakingtypes.Validator{}, []int64{}, 1) + SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 1, []stakingtypes.Validator{}, 1) expectations := GetMocksForCreateConsumerClient(ctx, &mocks, "chainID", clienttypes.NewHeight(4, 5)) @@ -259,10 +253,6 @@ func TestProviderStateIsCleanedAfterConsumerChainIsStopped(t *testing.T, ctx sdk require.False(t, found) acks := providerKeeper.GetSlashAcks(ctx, expectedChainID) require.Empty(t, acks) - _, found = providerKeeper.GetInitTimeoutTimestamp(ctx, expectedChainID) - require.False(t, found) - - 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) @@ -297,11 +287,36 @@ func GetTestConsumerAdditionProp() *providertypes.ConsumerAdditionProposal { 0, nil, nil, + 0, + false, ).(*providertypes.ConsumerAdditionProposal) return prop } +func GetTestMsgConsumerAddition() providertypes.MsgConsumerAddition { + return providertypes.MsgConsumerAddition{ + ChainId: "a ChainID", + InitialHeight: clienttypes.NewHeight(4, 5), + GenesisHash: []byte(base64.StdEncoding.EncodeToString([]byte("gen_hash"))), + BinaryHash: []byte(base64.StdEncoding.EncodeToString([]byte("bin_hash"))), + SpawnTime: time.Now(), + UnbondingPeriod: types.DefaultConsumerUnbondingPeriod, + CcvTimeoutPeriod: types.DefaultCCVTimeoutPeriod, + TransferTimeoutPeriod: types.DefaultTransferTimeoutPeriod, + ConsumerRedistributionFraction: types.DefaultConsumerRedistributeFrac, + BlocksPerDistributionTransmission: types.DefaultBlocksPerDistributionTransmission, + HistoricalEntries: types.DefaultHistoricalEntries, + DistributionTransmissionChannel: "", + Top_N: 10, + ValidatorsPowerCap: 0, + ValidatorSetCap: 0, + Allowlist: nil, + Denylist: nil, + Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + } +} + // Obtains a CrossChainValidator with a newly generated key, and randomized field values func GetNewCrossChainValidator(t *testing.T) consumertypes.CrossChainValidator { t.Helper() @@ -313,3 +328,13 @@ func GetNewCrossChainValidator(t *testing.T) consumertypes.CrossChainValidator { require.NoError(t, err) return validator } + +// Must panics if err is not nil, otherwise returns v. +// This is useful to get a value from a function that returns a value and an error +// in a single line. +func Must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v +} diff --git a/x/ccv/consumer/keeper/changeover_test.go b/x/ccv/consumer/keeper/changeover_test.go index b9b160ffec..ed76d956c0 100644 --- a/x/ccv/consumer/keeper/changeover_test.go +++ b/x/ccv/consumer/keeper/changeover_test.go @@ -29,8 +29,6 @@ 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()}, @@ -106,7 +104,6 @@ func TestChangeoverToConsumer(t *testing.T) { mocks.MockStakingKeeper, 180, // max validators tc.lastSovVals, - powers, -1) // any times // Add ref to standalone staking keeper diff --git a/x/ccv/consumer/keeper/keeper.go b/x/ccv/consumer/keeper/keeper.go index 644cfc33a7..0edfa64d17 100644 --- a/x/ccv/consumer/keeper/keeper.go +++ b/x/ccv/consumer/keeper/keeper.go @@ -387,7 +387,7 @@ func (k Keeper) GetElapsedPacketMaturityTimes(ctx sdk.Context) (maturingVSCPacke // GetAllPacketMaturityTimes returns a slice of all PacketMaturityTimes, sorted by maturity times. // // Note that PacketMaturityTimes are stored under keys with the following format: -// PacketMaturityTimeBytePrefix | maturityTime.UnixNano() | vscID +// PacketMaturityTimeKeyPrefix | maturityTime.UnixNano() | vscID // Thus, the returned array is in ascending order of maturityTimes. // If two entries have the same maturityTime, then they are ordered by vscID. func (k Keeper) GetAllPacketMaturityTimes(ctx sdk.Context) (maturingVSCPackets []types.MaturingVSCPacket) { @@ -489,7 +489,7 @@ func (k Keeper) DeleteHeightValsetUpdateID(ctx sdk.Context, height uint64) { // GetAllHeightToValsetUpdateIDs returns a list of all the block heights to valset update IDs in the store // // Note that the block height to vscID mapping is stored under keys with the following format: -// HeightValsetUpdateIDBytePrefix | height +// HeightValsetUpdateIDKeyPrefix | height // Thus, the returned array is in ascending order of heights. func (k Keeper) GetAllHeightToValsetUpdateIDs(ctx sdk.Context) (heightToValsetUpdateIDs []types.HeightToValsetUpdateID) { store := ctx.KVStore(k.storeKey) @@ -531,7 +531,7 @@ func (k Keeper) DeleteOutstandingDowntime(ctx sdk.Context, address sdk.ConsAddre // GetAllOutstandingDowntimes gets an array of the validator addresses of outstanding downtime flags // // Note that the outstanding downtime flags are stored under keys with the following format: -// OutstandingDowntimeBytePrefix | consAddress +// OutstandingDowntimeKeyPrefix | consAddress // Thus, the returned array is in ascending order of consAddresses. func (k Keeper) GetAllOutstandingDowntimes(ctx sdk.Context) (downtimes []types.OutstandingDowntime) { store := ctx.KVStore(k.storeKey) @@ -580,7 +580,7 @@ func (k Keeper) DeleteCCValidator(ctx sdk.Context, addr []byte) { // GetAllCCValidator returns all cross-chain validators // // Note that the cross-chain validators are stored under keys with the following format: -// CrossChainValidatorBytePrefix | address +// CrossChainValidatorKeyPrefix | address // Thus, the returned array is in ascending order of addresses. func (k Keeper) GetAllCCValidator(ctx sdk.Context) (validators []types.CrossChainValidator) { store := ctx.KVStore(k.storeKey) @@ -716,5 +716,9 @@ func (k Keeper) IsPrevStandaloneChain(ctx sdk.Context) bool { // 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, error) { - return ccv.GetLastBondedValidatorsUtil(ctx, k.standaloneStakingKeeper, k.Logger(ctx)) + maxVals, err := k.standaloneStakingKeeper.MaxValidators(ctx) + if err != nil { + return nil, err + } + return ccv.GetLastBondedValidatorsUtil(ctx, k.standaloneStakingKeeper, k.Logger(ctx), maxVals) } diff --git a/x/ccv/consumer/keeper/keeper_test.go b/x/ccv/consumer/keeper/keeper_test.go index ea90b2002f..3857c38c25 100644 --- a/x/ccv/consumer/keeper/keeper_test.go +++ b/x/ccv/consumer/keeper/keeper_test.go @@ -188,7 +188,6 @@ func TestGetLastSovereignValidators(t *testing.T) { mocks.MockStakingKeeper, 180, []stakingtypes.Validator{val}, - []int64{1000}, 1, ) diff --git a/x/ccv/consumer/module.go b/x/ccv/consumer/module.go index a175f2e450..a162653abd 100644 --- a/x/ccv/consumer/module.go +++ b/x/ccv/consumer/module.go @@ -172,7 +172,10 @@ func (am AppModule) BeginBlock(goCtx context.Context) error { am.keeper.SetHeightValsetUpdateID(ctx, blockHeight+1, vID) am.keeper.Logger(ctx).Debug("block height was mapped to vscID", "height", blockHeight+1, "vscID", vID) - am.keeper.TrackHistoricalInfo(ctx) + err := am.keeper.TrackHistoricalInfo(ctx) + if err != nil { + am.keeper.Logger(ctx).Warn("failed to track historical info", "error", err) + } return nil } diff --git a/x/ccv/consumer/types/codec.go b/x/ccv/consumer/types/codec.go index 243fc63f46..947fb4feb0 100644 --- a/x/ccv/consumer/types/codec.go +++ b/x/ccv/consumer/types/codec.go @@ -9,7 +9,6 @@ import ( // RegisterInterfaces registers the consumer Tx message types to the interface registry func RegisterInterfaces(registry codectypes.InterfaceRegistry) { - registry.RegisterImplementations( (*sdk.Msg)(nil), &MsgUpdateParams{}, diff --git a/x/ccv/democracy/distribution/module.go b/x/ccv/democracy/distribution/module.go index 310e542b6a..95d1cef6cd 100644 --- a/x/ccv/democracy/distribution/module.go +++ b/x/ccv/democracy/distribution/module.go @@ -127,12 +127,12 @@ func (am AppModule) AllocateTokens( representativesFraction := math.LegacyOneDec().Sub(communityTax) // allocate tokens proportionally to representatives voting power - vs.IterateBondedValidatorsByPower(ctx, func(_ int64, validator stakingtypes.ValidatorI) bool { + _ = vs.IterateBondedValidatorsByPower(ctx, func(_ int64, validator stakingtypes.ValidatorI) bool { // we get this validator's percentage of the total power by dividing their tokens by the total bonded tokens powerFraction := math.LegacyNewDecFromInt(validator.GetTokens()).QuoTruncate(math.LegacyNewDecFromInt(totalBondedTokens)) // we truncate here again, which means that the reward will be slightly lower than it should be reward := feesCollected.MulDecTruncate(representativesFraction).MulDecTruncate(powerFraction) - am.keeper.AllocateTokensToValidator(ctx, validator, reward) + _ = am.keeper.AllocateTokensToValidator(ctx, validator, reward) remaining = remaining.Sub(reward) return false diff --git a/x/ccv/democracy/governance/module.go b/x/ccv/democracy/governance/module.go index c4bf930066..f9312d7fa2 100644 --- a/x/ccv/democracy/governance/module.go +++ b/x/ccv/democracy/governance/module.go @@ -74,7 +74,6 @@ func (am AppModule) EndBlock(c context.Context) error { deleteForbiddenProposal(ctx, am, proposal) return false, nil }) - if err != nil { return err } @@ -113,14 +112,41 @@ func deleteForbiddenProposal(ctx sdk.Context, am AppModule, proposal govv1.Propo return } + logger := am.keeper.Logger(ctx) + // delete the votes related to the proposal calling Tally // Tally's return result won't be used in decision if the tokens will be burned or refunded (they are always refunded), but // this function needs to be called to delete the votes related to the given proposal, since the deleteVote function is // private and cannot be called directly from the overridden app module - am.keeper.Tally(ctx, proposal) + _, _, _, err := am.keeper.Tally(ctx, proposal) + if err != nil { + logger.Warn( + "failed to tally disallowed proposal", + "proposal", proposal.Id, + "title", proposal.GetTitle(), + "total_deposit", proposal.TotalDeposit) + return + } + + err = am.keeper.DeleteProposal(ctx, proposal.Id) + if err != nil { + logger.Warn( + "failed to delete disallowed proposal", + "proposal", proposal.Id, + "title", proposal.GetTitle(), + "total_deposit", proposal.TotalDeposit) + return + } - am.keeper.DeleteProposal(ctx, proposal.Id) - am.keeper.RefundAndDeleteDeposits(ctx, proposal.Id) + err = am.keeper.RefundAndDeleteDeposits(ctx, proposal.Id) + if err != nil { + logger.Warn( + "failed to refund deposits for disallowed proposal", + "proposal", proposal.Id, + "title", proposal.GetTitle(), + "total_deposit", proposal.TotalDeposit) + return + } ctx.EventManager().EmitEvent( sdk.NewEvent( @@ -130,9 +156,8 @@ func deleteForbiddenProposal(ctx sdk.Context, am AppModule, proposal govv1.Propo ), ) - logger := am.keeper.Logger(ctx) logger.Info( - "proposal is not whitelisted; deleted", + "proposal is not allowed; deleted", "proposal", proposal.Id, "title", proposal.GetTitle(), "total_deposit", proposal.TotalDeposit) diff --git a/x/ccv/no_valupdates_genutil/doc.go b/x/ccv/no_valupdates_genutil/doc.go new file mode 100644 index 0000000000..670872a700 --- /dev/null +++ b/x/ccv/no_valupdates_genutil/doc.go @@ -0,0 +1,8 @@ +/* +Package genutil defines a "wrapper" module around the Cosmos SDK's native +x/genutil module. In other words, it provides the exact same functionality as +the native module in that it simply embeds the native module. However, it +overrides `InitGenesis` which will return no validator set updates. Instead, +it is assumed that some other module will provide the validator set updates. +*/ +package genutil diff --git a/x/ccv/no_valupdates_genutil/module.go b/x/ccv/no_valupdates_genutil/module.go new file mode 100644 index 0000000000..9a7528c071 --- /dev/null +++ b/x/ccv/no_valupdates_genutil/module.go @@ -0,0 +1,58 @@ +package genutil + +import ( + "encoding/json" + + "cosmossdk.io/core/genesis" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/genutil" + "github.com/cosmos/cosmos-sdk/x/genutil/types" + + abci "github.com/cometbft/cometbft/abci/types" +) + +var ( + _ module.AppModuleGenesis = AppModule{} + _ module.AppModuleBasic = genutil.AppModuleBasic{} +) + +// AppModule implements an application module for the genutil module. +type AppModule struct { + genutil.AppModule + + stakingKeeper types.StakingKeeper + deliverTx genesis.TxHandler + txEncodingConfig client.TxEncodingConfig +} + +// NewAppModule creates a new AppModule object +func NewAppModule(accountKeeper types.AccountKeeper, + stakingKeeper types.StakingKeeper, deliverTx genesis.TxHandler, + txEncodingConfig client.TxEncodingConfig, +) module.GenesisOnlyAppModule { + genutilAppModule := genutil.NewAppModule(accountKeeper, stakingKeeper, deliverTx, txEncodingConfig) + genutilAppModule.AppModuleGenesis = AppModule{ + AppModule: genutilAppModule.AppModuleGenesis.(genutil.AppModule), + stakingKeeper: stakingKeeper, + deliverTx: deliverTx, + txEncodingConfig: txEncodingConfig, + } + return genutilAppModule +} + +// InitGenesis delegates the InitGenesis call to the underlying x/genutil module, +// however, it returns no validator updates as validator updates will be provided by the provider module. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + + cdc.MustUnmarshalJSON(data, &genesisState) + _, err := genutil.InitGenesis(ctx, am.stakingKeeper, am.deliverTx, genesisState, am.txEncodingConfig) + if err != nil { + panic(err) + } + + return []abci.ValidatorUpdate{} +} diff --git a/x/ccv/no_valupdates_staking/doc.go b/x/ccv/no_valupdates_staking/doc.go new file mode 100644 index 0000000000..3c2863c476 --- /dev/null +++ b/x/ccv/no_valupdates_staking/doc.go @@ -0,0 +1,8 @@ +/* +Package staking defines a "wrapper" module around the Cosmos SDK's native +x/staking module. In other words, it provides the exact same functionality as +the native module in that it simply embeds the native module. However, it +overrides `EndBlock` and `InitGenesis`, which will return no validator set updates. Instead, +it is assumed that some other module will provide the validator set updates. +*/ +package staking diff --git a/x/ccv/no_valupdates_staking/module.go b/x/ccv/no_valupdates_staking/module.go new file mode 100644 index 0000000000..cda755621e --- /dev/null +++ b/x/ccv/no_valupdates_staking/module.go @@ -0,0 +1,84 @@ +package staking + +import ( + "context" + "encoding/json" + + "cosmossdk.io/core/appmodule" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/staking/exported" + "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/types" + + abci "github.com/cometbft/cometbft/abci/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModule{} + + _ module.HasServices = AppModule{} + _ module.HasInvariants = AppModule{} + _ module.HasABCIGenesis = AppModule{} + _ module.HasABCIEndBlock = AppModule{} + + _ appmodule.AppModule = AppModule{} + _ appmodule.HasBeginBlocker = AppModule{} +) + +// AppModule embeds the Cosmos SDK's x/staking AppModuleBasic. +type AppModuleBasic struct { + staking.AppModuleBasic +} + +// AppModule embeds the Cosmos SDK's x/staking AppModule where we only override +// specific methods. +type AppModule struct { + // embed the Cosmos SDK's x/staking AppModule + staking.AppModule + + keeper keeper.Keeper + accKeeper types.AccountKeeper + bankKeeper types.BankKeeper +} + +// NewAppModule creates a new AppModule object using the native x/staking module +// AppModule constructor. +func NewAppModule( + cdc codec.Codec, + keeper *keeper.Keeper, + ak types.AccountKeeper, + bk types.BankKeeper, + ls exported.Subspace, +) AppModule { + stakingAppMod := staking.NewAppModule(cdc, keeper, ak, bk, ls) + return AppModule{ + AppModule: stakingAppMod, + keeper: *keeper, + accKeeper: ak, + bankKeeper: bk, + } +} + +// InitGenesis delegates the InitGenesis call to the underlying x/staking module, +// however, it returns no validator updates as validator updates will be provided by the provider module. +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) + + return []abci.ValidatorUpdate{} +} + +// EndBlock delegates the EndBlock call to the underlying x/staking module, +// however, it returns no validator updates as validator updates will be provided by the provider module. +func (am AppModule) EndBlock(ctx context.Context) ([]abci.ValidatorUpdate, error) { + _, err := am.keeper.EndBlocker(ctx) + // return the error, but no validator updates + return []abci.ValidatorUpdate{}, err +} diff --git a/x/ccv/provider/client/cli/query.go b/x/ccv/provider/client/cli/query.go index 697fccbb76..84fbff3fd0 100644 --- a/x/ccv/provider/client/cli/query.go +++ b/x/ccv/provider/client/cli/query.go @@ -39,7 +39,7 @@ func NewQueryCmd() *cobra.Command { cmd.AddCommand(CmdConsumerValidators()) cmd.AddCommand(CmdConsumerChainsValidatorHasToValidate()) cmd.AddCommand(CmdValidatorConsumerCommissionRate()) - cmd.AddCommand(CmdOldestUnconfirmedVsc()) + cmd.AddCommand(CmdBlocksUntilNextEpoch()) return cmd } @@ -570,11 +570,11 @@ $ %s validator-consumer-commission-rate foochain %s1gghjut3ccd8ay0zduzj64hwre2fx return cmd } -func CmdOldestUnconfirmedVsc() *cobra.Command { +func CmdBlocksUntilNextEpoch() *cobra.Command { cmd := &cobra.Command{ - Use: "oldest_unconfirmed_vsc [chainid]", - Short: "Query the send timestamp of the oldest unconfirmed VSCPacket by chain id", - Args: cobra.ExactArgs(1), + Use: "blocks-until-next-epoch", + Short: "Query the number of blocks until the next epoch begins and validator updates are sent to consumer chains", + Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { @@ -582,13 +582,13 @@ func CmdOldestUnconfirmedVsc() *cobra.Command { } queryClient := types.NewQueryClient(clientCtx) - req := types.QueryOldestUnconfirmedVscRequest{ChainId: args[0]} - res, err := queryClient.QueryOldestUnconfirmedVsc(cmd.Context(), &req) + req := &types.QueryBlocksUntilNextEpochRequest{} + res, err := queryClient.QueryBlocksUntilNextEpoch(cmd.Context(), req) if err != nil { return err } - return clientCtx.PrintProto(&res.VscSendTimestamp) + return clientCtx.PrintProto(res) }, } diff --git a/x/ccv/provider/client/cli/tx.go b/x/ccv/provider/client/cli/tx.go index eadb3a7e57..4011a36a79 100644 --- a/x/ccv/provider/client/cli/tx.go +++ b/x/ccv/provider/client/cli/tx.go @@ -325,7 +325,8 @@ func NewSetConsumerCommissionRateCmd() *cobra.Command { if err != nil { return err } - msg := types.NewMsgSetConsumerCommissionRate(args[0], commission, sdk.ValAddress(providerValAddr)) + signer := clientCtx.GetFromAddress().String() + msg := types.NewMsgSetConsumerCommissionRate(args[0], commission, sdk.ValAddress(providerValAddr), signer) if err := msg.ValidateBasic(); err != nil { return err } diff --git a/x/ccv/provider/client/legacy_proposal_handler.go b/x/ccv/provider/client/legacy_proposal_handler.go deleted file mode 100644 index f837e7bab2..0000000000 --- a/x/ccv/provider/client/legacy_proposal_handler.go +++ /dev/null @@ -1,286 +0,0 @@ -package client - -import ( - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/tx" - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - govclient "github.com/cosmos/cosmos-sdk/x/gov/client" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" - - "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" -) - -var ( - ConsumerAdditionProposalHandler = govclient.NewProposalHandler(SubmitConsumerAdditionPropTxCmd) - ConsumerRemovalProposalHandler = govclient.NewProposalHandler(SubmitConsumerRemovalProposalTxCmd) - ChangeRewardDenomsProposalHandler = govclient.NewProposalHandler(SubmitChangeRewardDenomsProposalTxCmd) - ConsumerModificationProposalHandler = govclient.NewProposalHandler(SubmitConsumerModificationProposalTxCmd) -) - -// SubmitConsumerAdditionPropTxCmd returns a CLI command handler for submitting -// a consumer addition proposal via a transaction. -func SubmitConsumerAdditionPropTxCmd() *cobra.Command { - return &cobra.Command{ - Use: "consumer-addition [proposal-file]", - Args: cobra.ExactArgs(1), - Short: "Submit a consumer addition proposal", - Long: ` -Submit a consumer addition proposal along with an initial deposit. -The proposal details must be supplied via a JSON file. -Unbonding period, transfer timeout period and ccv timeout period should be provided as nanosecond time periods. - -Example: -$ tx gov submit-legacy-proposal consumer-addition --from= - -Where proposal.json contains: - -{ - "title": "Create the FooChain", - "summary": "Gonna be a great chain", - "chain_id": "foochain", - "initial_height": { - "revision_number": 2, - "revision_height": 3 - }, - "genesis_hash": "Z2VuZXNpcyBoYXNo", - "binary_hash": "YmluYXJ5IGhhc2g=", - "spawn_time": "2022-01-27T15:59:50.121607-08:00", - "blocks_per_distribution_transmission": 1000, - "consumer_redistribution_fraction": "0.75", - "distribution_transmission_channel": "", - "historical_entries": 10000, - "transfer_timeout_period": 3600000000000, - "ccv_timeout_period": 2419200000000000, - "unbonding_period": 1728000000000000, - "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 { - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - proposal, err := ParseConsumerAdditionProposalJSON(args[0]) - if err != nil { - return err - } - - // do not fail for errors regarding the unbonding period, but just log a warning - CheckPropUnbondingPeriod(clientCtx, proposal.UnbondingPeriod) - - content := types.NewConsumerAdditionProposal( - proposal.Title, proposal.Summary, proposal.ChainId, proposal.InitialHeight, - proposal.GenesisHash, proposal.BinaryHash, proposal.SpawnTime, - proposal.ConsumerRedistributionFraction, proposal.BlocksPerDistributionTransmission, - proposal.DistributionTransmissionChannel, proposal.HistoricalEntries, - proposal.CcvTimeoutPeriod, proposal.TransferTimeoutPeriod, proposal.UnbondingPeriod, 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, false) - if err != nil { - return err - } - - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } -} - -// SubmitConsumerRemovalPropTxCmd returns a CLI command handler for submitting -// a consumer addition proposal via a transaction. -func SubmitConsumerRemovalProposalTxCmd() *cobra.Command { - return &cobra.Command{ - Use: "consumer-removal [proposal-file]", - Args: cobra.ExactArgs(1), - Short: "Submit a consumer chain removal proposal", - Long: ` -Submit a consumer chain removal proposal along with an initial deposit. -The proposal details must be supplied via a JSON file. - -Example: -$ tx gov submit-legacy-proposal consumer-removal --from= - -Where proposal.json contains: -{ - "title": "Stop the FooChain", - "summary": "It was a great chain", - "chain_id": "foochain", - "stop_time": "2022-01-27T15:59:50.121607-08:00", - "deposit": "10000stake" -} - `, RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - proposal, err := ParseConsumerRemovalProposalJSON(args[0]) - if err != nil { - return err - } - - content := types.NewConsumerRemovalProposal(proposal.Title, proposal.Summary, proposal.ChainId, proposal.StopTime) - from := clientCtx.GetFromAddress() - - msgContent, err := govv1.NewLegacyContent(content, authtypes.NewModuleAddress(govtypes.ModuleName).String()) - if err != nil { - return err - } - - deposit, err := sdk.ParseCoinsNormalized(proposal.Deposit) - if err != nil { - return err - } - - msg, err := govv1.NewMsgSubmitProposal([]sdk.Msg{msgContent}, deposit, from.String(), "", content.GetTitle(), proposal.Summary, false) - 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 { - return &cobra.Command{ - Use: "change-reward-denoms [proposal-file]", - Args: cobra.ExactArgs(1), - Short: "Submit a change reward denoms proposal", - Long: `Submit an change reward denoms proposal with an initial deposit. - The proposal details must be supplied via a JSON file. - - Example: - $ tx gov submit-legacy-proposal change-reward-denoms --from= - - Where proposal.json contains: - { - "title": "Change reward denoms", - "summary": "Change reward denoms", - "denoms_to_add": ["untrn"], - "denoms_to_remove": ["stake"], - "deposit": "10000stake" - } - `, - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - proposal, err := ParseChangeRewardDenomsProposalJSON(args[0]) - if err != nil { - return err - } - - content := types.NewChangeRewardDenomsProposal(proposal.Title, proposal.Summary, proposal.DenomsToAdd, proposal.DenomsToRemove) - - from := clientCtx.GetFromAddress() - - msgContent, err := govv1.NewLegacyContent(content, authtypes.NewModuleAddress(govtypes.ModuleName).String()) - if err != nil { - return err - } - - deposit, err := sdk.ParseCoinsNormalized(proposal.Deposit) - if err != nil { - return err - } - - msg, err := govv1.NewMsgSubmitProposal([]sdk.Msg{msgContent}, deposit, from.String(), "", content.GetTitle(), proposal.Summary, false) - if err != nil { - return err - } - - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } -} - -// 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, false) - if err != nil { - return err - } - - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } -} diff --git a/x/ccv/provider/client/legacy_proposals.go b/x/ccv/provider/client/legacy_proposals.go index b19528b1b7..ef9484540d 100644 --- a/x/ccv/provider/client/legacy_proposals.go +++ b/x/ccv/provider/client/legacy_proposals.go @@ -41,6 +41,8 @@ type ConsumerAdditionProposalJSON struct { ValidatorSetCap uint32 `json:"validator_set_cap"` Allowlist []string `json:"allowlist"` Denylist []string `json:"denylist"` + MinStake uint64 `json:"min_stake"` + AllowInactiveVals bool `json:"allow_inactive_vals"` } type ConsumerAdditionProposalReq struct { @@ -172,6 +174,8 @@ type ConsumerModificationProposalJSON struct { ValidatorSetCap uint32 `json:"validator_set_cap"` Allowlist []string `json:"allowlist"` Denylist []string `json:"denylist"` + MinStake uint64 `json:"min_stake"` + AllowInactiveVals bool `json:"allow_inactive_vals"` Deposit string `json:"deposit"` } diff --git a/x/ccv/provider/ibc_module.go b/x/ccv/provider/ibc_module.go index f91f69e618..b41512e98a 100644 --- a/x/ccv/provider/ibc_module.go +++ b/x/ccv/provider/ibc_module.go @@ -197,13 +197,8 @@ func (am AppModule) OnRecvPacket( var err error switch consumerPacket.Type { case ccv.VscMaturedPacket: - // handle VSCMaturedPacket - data := *consumerPacket.GetVscMaturedPacketData() - err = am.keeper.OnRecvVSCMaturedPacket(ctx, packet, data) - if err == nil { - logger.Info("successfully handled VSCMaturedPacket", "sequence", packet.Sequence) - eventAttributes = append(eventAttributes, sdk.NewAttribute(ccv.AttributeValSetUpdateID, strconv.Itoa(int(data.ValsetUpdateId)))) - } + // ignore VSCMaturedPacket + err = nil case ccv.SlashPacket: // handle SlashPacket var ackResult ccv.PacketAckResult diff --git a/x/ccv/provider/ibc_module_test.go b/x/ccv/provider/ibc_module_test.go index fdae2a03ba..58573d3e3b 100644 --- a/x/ccv/provider/ibc_module_test.go +++ b/x/ccv/provider/ibc_module_test.go @@ -31,7 +31,7 @@ func TestOnChanOpenInit(t *testing.T) { providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx( t, keeperParams) defer ctrl.Finish() - providerModule := provider.NewAppModule(&providerKeeper, *keeperParams.ParamsSubspace) + providerModule := provider.NewAppModule(&providerKeeper, *keeperParams.ParamsSubspace, keeperParams.StoreKey) // OnChanOpenInit must error for provider even with correct arguments _, err := providerModule.OnChanOpenInit( @@ -119,7 +119,7 @@ func TestOnChanOpenTry(t *testing.T) { keeperParams := testkeeper.NewInMemKeeperParams(t) providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx( t, keeperParams) - providerModule := provider.NewAppModule(&providerKeeper, *keeperParams.ParamsSubspace) + providerModule := provider.NewAppModule(&providerKeeper, *keeperParams.ParamsSubspace, keeperParams.StoreKey) providerKeeper.SetPort(ctx, ccv.ProviderPortID) providerKeeper.SetConsumerClientId(ctx, "consumerChainID", "clientIDToConsumer") @@ -190,7 +190,7 @@ func TestOnChanOpenAck(t *testing.T) { providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx( t, keeperParams) defer ctrl.Finish() - providerModule := provider.NewAppModule(&providerKeeper, *keeperParams.ParamsSubspace) + providerModule := provider.NewAppModule(&providerKeeper, *keeperParams.ParamsSubspace, keeperParams.StoreKey) // OnChanOpenAck must error for provider even with correct arguments err := providerModule.OnChanOpenAck( @@ -312,7 +312,7 @@ func TestOnChanOpenConfirm(t *testing.T) { providerKeeper.SetChainToChannel(ctx, "consumerChainID", "existingChannelID") } - providerModule := provider.NewAppModule(&providerKeeper, *keeperParams.ParamsSubspace) + providerModule := provider.NewAppModule(&providerKeeper, *keeperParams.ParamsSubspace, keeperParams.StoreKey) err := providerModule.OnChanOpenConfirm(ctx, "providerPortID", "channelID") diff --git a/x/ccv/provider/keeper/distribution.go b/x/ccv/provider/keeper/distribution.go index ca16831ee8..7970a8c682 100644 --- a/x/ccv/provider/keeper/distribution.go +++ b/x/ccv/provider/keeper/distribution.go @@ -1,11 +1,11 @@ package keeper import ( + "context" + storetypes "cosmossdk.io/store/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" - "context" - errorsmod "cosmossdk.io/errors" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" @@ -56,7 +56,7 @@ func (k Keeper) DeleteConsumerRewardDenom( func (k Keeper) GetAllConsumerRewardDenoms(ctx sdk.Context) (consumerRewardDenoms []string) { store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.ConsumerRewardDenomsBytePrefix}) + iterator := storetypes.KVStorePrefixIterator(store, types.ConsumerRewardDenomsKeyPrefix()) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { key := iterator.Key()[1:] @@ -191,7 +191,17 @@ func (k Keeper) AllocateTokensToConsumerValidators( } // Allocate tokens by iterating over the consumer validators - for _, consumerVal := range k.GetConsumerValSet(ctx, chainID) { + consumerVals, err := k.GetConsumerValSet(ctx, chainID) + if err != nil { + k.Logger(ctx).Error( + "cannot get consumer validator set while allocating rewards from consumer chain", + chainID, + "error", + err, + ) + return allocated + } + for _, consumerVal := range consumerVals { // if a validator is not eligible, this means that the other eligible validators would get more rewards if !k.IsEligibleForConsumerRewards(ctx, consumerVal.JoinHeight) { continue @@ -272,7 +282,17 @@ func (k Keeper) GetConsumerRewardsPool(ctx sdk.Context) sdk.Coins { // for the given consumer chain 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) { + vals, err := k.GetConsumerValSet(ctx, chainID) + if err != nil { + k.Logger(ctx).Error( + "cannot get consumer validator set while computing total voting power for consumer chain", + chainID, + "error", + err, + ) + return + } + for _, v := range vals { // 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) { diff --git a/x/ccv/provider/keeper/distribution_test.go b/x/ccv/provider/keeper/distribution_test.go index ba79458e00..e8caeb7c36 100644 --- a/x/ccv/provider/keeper/distribution_test.go +++ b/x/ccv/provider/keeper/distribution_test.go @@ -16,7 +16,6 @@ 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" ) @@ -54,7 +53,7 @@ func TestComputeConsumerTotalVotingPower(t *testing.T) { keeper.SetConsumerValidator( ctx, chainID, - types.ConsumerValidator{ + providertypes.ConsensusValidator{ ProviderConsAddr: val.Address, Power: val.VotingPower, }, diff --git a/x/ccv/provider/keeper/genesis.go b/x/ccv/provider/keeper/genesis.go index b965e3ed57..5b44227f90 100644 --- a/x/ccv/provider/keeper/genesis.go +++ b/x/ccv/provider/keeper/genesis.go @@ -5,12 +5,14 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" + abci "github.com/cometbft/cometbft/abci/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. -func (k Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { +func (k Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) []abci.ValidatorUpdate { k.SetPort(ctx, ccv.ProviderPortID) // Only try to bind to port if it is not already bound, since we may already own @@ -39,15 +41,6 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { p := prop k.SetPendingConsumerRemovalProp(ctx, &p) } - for _, ubdOp := range genState.UnbondingOps { - k.SetUnbondingOp(ctx, ubdOp) - } - - // Note that MatureUnbondingOps aren't stored across blocks, but it - // might be used after implementing standalone to consumer transition - if genState.MatureUnbondingOps != nil { - k.AppendMaturedUnbondingOps(ctx, genState.MatureUnbondingOps.Ids) - } // Set initial state for each consumer chain for _, cs := range genState.ConsumerStates { @@ -58,9 +51,6 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { // the ConsumerGenesis validated in ConsumerState.Validate(). panic(fmt.Errorf("consumer chain genesis could not be persisted: %w", err)) } - for _, ubdOpIndex := range cs.UnbondingOpsIndex { - k.SetUnbondingOpIndex(ctx, chainID, ubdOpIndex.GetVscId(), ubdOpIndex.GetUnbondingOpIds()) - } // check if the CCV channel was established if cs.ChannelId != "" { k.SetChannelToChain(ctx, cs.ChannelId, chainID) @@ -84,25 +74,56 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { k.SetValidatorByConsumerAddr(ctx, item.ChainId, consumerAddr, providerAddr) } - for _, item := range genState.ConsumerAddrsToPrune { + for _, item := range genState.ConsumerAddrsToPruneV2 { for _, addr := range item.ConsumerAddrs.Addresses { consumerAddr := types.NewConsumerConsAddress(addr) - k.AppendConsumerAddrsToPrune(ctx, item.ChainId, item.VscId, consumerAddr) + k.AppendConsumerAddrsToPrune(ctx, item.ChainId, item.PruneTs, consumerAddr) } } - for _, item := range genState.InitTimeoutTimestamps { - k.SetInitTimeoutTimestamp(ctx, item.ChainId, item.Timestamp) + k.SetParams(ctx, genState.Params) + k.InitializeSlashMeter(ctx) + + return k.InitGenesisValUpdates(ctx) +} + +// InitGenesisValUpdates returns the genesis validator set updates +// for the provider module by selecting the first MaxProviderConsensusValidators +// from the staking module's validator set. +func (k Keeper) InitGenesisValUpdates(ctx sdk.Context) []abci.ValidatorUpdate { + // get the staking validator set + valSet, err := k.stakingKeeper.GetBondedValidatorsByPower(ctx) + if err != nil { + panic(fmt.Errorf("retrieving validator set: %w", err)) } - for _, item := range genState.ExportedVscSendTimestamps { - for _, vscSendTimestamp := range item.VscSendTimestamps { - k.SetVscSendTimestamp(ctx, item.ChainId, vscSendTimestamp.VscId, vscSendTimestamp.Timestamp) + // restrict the set to the first MaxProviderConsensusValidators + maxVals := k.GetMaxProviderConsensusValidators(ctx) + if int64(len(valSet)) > maxVals { + k.Logger(ctx).Info(fmt.Sprintf("reducing validator set from %d to %d", len(valSet), maxVals)) + valSet = valSet[:maxVals] + } + + reducedValSet := make([]types.ConsensusValidator, len(valSet)) + for i, val := range valSet { + consensusVal, err := k.CreateProviderConsensusValidator(ctx, val) + if err != nil { + k.Logger(ctx).Error(fmt.Sprintf("failed to create provider consensus validator: %v", err)) + continue } + reducedValSet[i] = consensusVal } - k.SetParams(ctx, genState.Params) - k.InitializeSlashMeter(ctx) + k.SetLastProviderConsensusValSet(ctx, reducedValSet) + + valUpdates := make([]abci.ValidatorUpdate, len(reducedValSet)) + for i, val := range reducedValSet { + valUpdates[i] = abci.ValidatorUpdate{ + PubKey: *val.PublicKey, + Power: val.Power, + } + } + return valUpdates } // ExportGenesis returns the CCV provider module's exported genesis @@ -110,7 +131,6 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { // get a list of all registered consumer chains registeredChainIDs := k.GetAllRegisteredConsumerChainIDs(ctx) - var exportedVscSendTimestamps []types.ExportedVscSendTimestamp // export states for each consumer chains var consumerStates []types.ConsumerState for _, chainID := range registeredChainIDs { @@ -125,10 +145,9 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { // initial consumer chain states cs := types.ConsumerState{ - ChainId: chainID, - ClientId: clientID, - ConsumerGenesis: gen, - UnbondingOpsIndex: k.GetAllUnbondingOpIndexes(ctx, chainID), + ChainId: chainID, + ClientId: clientID, + ConsumerGenesis: gen, } // try to find channel id for the current consumer chain @@ -144,13 +163,10 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { cs.PendingValsetChanges = k.GetPendingVSCPackets(ctx, chainID) consumerStates = append(consumerStates, cs) - - 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{} + consumerAddrsToPrune := []types.ConsumerAddrsToPruneV2{} for _, chainID := range registeredChainIDs { consumerAddrsToPrune = append(consumerAddrsToPrune, k.GetAllConsumerAddrsToPrune(ctx, chainID)...) } @@ -161,15 +177,11 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { k.GetValidatorSetUpdateId(ctx), k.GetAllValsetUpdateBlockHeights(ctx), consumerStates, - k.GetAllUnbondingOps(ctx), - &types.MaturedUnbondingOps{Ids: k.GetMaturedUnbondingOps(ctx)}, k.GetAllPendingConsumerAdditionProps(ctx), k.GetAllPendingConsumerRemovalProps(ctx), params, k.GetAllValidatorConsumerPubKeys(ctx, nil), k.GetAllValidatorsByConsumerAddr(ctx, nil), consumerAddrsToPrune, - k.GetAllInitTimeoutTimestamps(ctx), - exportedVscSendTimestamps, ) } diff --git a/x/ccv/provider/keeper/genesis_test.go b/x/ccv/provider/keeper/genesis_test.go index 8fd22824ef..82069b4b8d 100644 --- a/x/ccv/provider/keeper/genesis_test.go +++ b/x/ccv/provider/keeper/genesis_test.go @@ -1,7 +1,6 @@ package keeper_test import ( - "sort" "testing" "time" @@ -11,6 +10,7 @@ import ( "github.com/stretchr/testify/require" sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/cosmos/interchain-security/v5/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" @@ -26,43 +26,19 @@ func TestInitAndExportGenesis(t *testing.T) { expClientID := "client" oneHourFromNow := time.Now().UTC().Add(time.Hour) initHeight, vscID := uint64(5), uint64(1) - ubdIndex := []uint64{0, 1, 2} params := providertypes.DefaultParams() // create validator keys and addresses for key assignment providerCryptoId := crypto.NewCryptoIdentityFromIntSeed(7896) provAddr := providerCryptoId.ProviderConsAddress() + provVal := providerCryptoId.SDKStakingValidator() + provPubKey, err := provVal.TmConsPublicKey() + require.NoError(t, err) consumerCryptoId := crypto.NewCryptoIdentityFromIntSeed(7897) consumerTmPubKey := consumerCryptoId.TMProtoCryptoPublicKey() consumerConsAddr := consumerCryptoId.ConsumerConsAddress() - initTimeoutTimeStamps := []providertypes.InitTimeoutTimestamp{ - {ChainId: cChainIDs[0], Timestamp: uint64(time.Now().UTC().UnixNano()) + 10}, - {ChainId: cChainIDs[1], Timestamp: uint64(time.Now().UTC().UnixNano()) + 15}, - } - - now := time.Now().UTC() - exportedVscSendTimeStampsC0 := providertypes.ExportedVscSendTimestamp{ - ChainId: "c0", - VscSendTimestamps: []providertypes.VscSendTimestamp{ - {VscId: 1, Timestamp: now.Add(time.Hour)}, - {VscId: 2, Timestamp: now.Add(2 * time.Hour)}, - }, - } - - exportedVscSendTimeStampsC1 := providertypes.ExportedVscSendTimestamp{ - ChainId: "c1", - VscSendTimestamps: []providertypes.VscSendTimestamp{ - {VscId: 1, Timestamp: now.Add(-time.Hour)}, - {VscId: 2, Timestamp: now.Add(time.Hour)}, - }, - } - - var exportedVscSendTimeStampsAll []providertypes.ExportedVscSendTimestamp - exportedVscSendTimeStampsAll = append(exportedVscSendTimeStampsAll, exportedVscSendTimeStampsC0) - exportedVscSendTimeStampsAll = append(exportedVscSendTimeStampsAll, exportedVscSendTimeStampsC1) - // create genesis struct provGenesis := providertypes.NewGenesisState(vscID, []providertypes.ValsetUpdateIdToHeight{{ValsetUpdateId: vscID, Height: initHeight}}, @@ -73,9 +49,6 @@ func TestInitAndExportGenesis(t *testing.T) { "channel", initHeight, *ccv.DefaultConsumerGenesisState(), - []providertypes.VscUnbondingOps{ - {VscId: vscID, UnbondingOpIds: ubdIndex}, - }, []ccv.ValidatorSetChangePacketData{}, []string{"slashedValidatorConsAddress"}, ), @@ -85,16 +58,10 @@ func TestInitAndExportGenesis(t *testing.T) { "", 0, *ccv.DefaultConsumerGenesisState(), - nil, []ccv.ValidatorSetChangePacketData{{ValsetUpdateId: vscID}}, nil, ), }, - []providertypes.UnbondingOp{{ - Id: vscID, - UnbondingConsumerChains: []string{cChainIDs[0]}, - }}, - &providertypes.MaturedUnbondingOps{Ids: ubdIndex}, []providertypes.ConsumerAdditionProposal{{ ChainId: cChainIDs[0], SpawnTime: oneHourFromNow, @@ -118,15 +85,13 @@ func TestInitAndExportGenesis(t *testing.T) { ConsumerAddr: consumerConsAddr.ToSdkConsAddr(), }, }, - []providertypes.ConsumerAddrsToPrune{ + []providertypes.ConsumerAddrsToPruneV2{ { ChainId: cChainIDs[0], - VscId: vscID, + PruneTs: oneHourFromNow, ConsumerAddrs: &providertypes.AddressList{Addresses: [][]byte{consumerConsAddr.ToSdkConsAddr()}}, }, }, - initTimeoutTimeStamps, - exportedVscSendTimeStampsAll, ) // Instantiate in-mem provider keeper with mocks @@ -141,6 +106,16 @@ func TestInitAndExportGenesis(t *testing.T) { ctx).Return(math.NewInt(100), nil).Times(1), // Return total voting power as 100 ) + mocks.MockStakingKeeper.EXPECT().GetBondedValidatorsByPower(gomock.Any()).Return( + []stakingtypes.Validator{ + provVal, + }, nil).AnyTimes() + + valAddr, err := sdk.ValAddressFromBech32(provVal.GetOperator()) + require.NoError(t, err) + mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), valAddr). + Return(int64(100), nil).AnyTimes() + // init provider chain pk.InitGenesis(ctx, provGenesis) @@ -157,11 +132,6 @@ func TestInitAndExportGenesis(t *testing.T) { require.Equal(t, expectedCandidate, pk.GetSlashMeterReplenishTimeCandidate(ctx)) // check local provider chain states - ubdOps, found := pk.GetUnbondingOp(ctx, vscID) - require.True(t, found) - require.Equal(t, provGenesis.UnbondingOps[0], ubdOps) - matureUbdOps := pk.GetMaturedUnbondingOps(ctx) - require.Equal(t, ubdIndex, matureUbdOps) chainID, found := pk.GetChannelToChain(ctx, provGenesis.ConsumerStates[0].ChannelId) require.True(t, found) require.Equal(t, cChainIDs[0], chainID) @@ -175,6 +145,17 @@ func TestInitAndExportGenesis(t *testing.T) { require.True(t, pk.PendingConsumerRemovalPropExists(ctx, cChainIDs[0], oneHourFromNow)) require.Equal(t, provGenesis.Params, pk.GetParams(ctx)) + providerConsensusValSet, err := pk.GetLastProviderConsensusValSet(ctx) + require.NoError(t, err) + require.Equal(t, + []providertypes.ConsensusValidator{{ + ProviderConsAddr: provAddr.Address, + Power: 100, + PublicKey: &provPubKey, + }}, + providerConsensusValSet, + ) + gotConsTmPubKey, found := pk.GetValidatorConsumerPubKey(ctx, cChainIDs[0], provAddr) require.True(t, found) require.Equal(t, consumerTmPubKey, gotConsTmPubKey) @@ -183,7 +164,7 @@ func TestInitAndExportGenesis(t *testing.T) { require.True(t, found) require.Equal(t, provAddr, providerAddr) - addrs := pk.GetConsumerAddrsToPrune(ctx, cChainIDs[0], vscID) + addrs := pk.GetConsumerAddrsToPrune(ctx, cChainIDs[0], oneHourFromNow) // Expect same list as what was provided in provGenesis expectedAddrList := providertypes.AddressList{Addresses: [][]byte{consumerConsAddr.ToSdkConsAddr()}} require.Equal(t, expectedAddrList, addrs) @@ -193,24 +174,6 @@ func TestInitAndExportGenesis(t *testing.T) { // check the exported genesis require.Equal(t, provGenesis, pk.ExportGenesis(ctx)) - - initTimeoutTimestampInStore := pk.GetAllInitTimeoutTimestamps(ctx) - sort.Slice(initTimeoutTimestampInStore, func(i, j int) bool { - return initTimeoutTimestampInStore[i].Timestamp < initTimeoutTimestampInStore[j].Timestamp - }) - require.Equal(t, initTimeoutTimestampInStore, initTimeoutTimeStamps) - - vscSendTimestampsC0InStore := pk.GetAllVscSendTimestamps(ctx, cChainIDs[0]) - sort.Slice(vscSendTimestampsC0InStore, func(i, j int) bool { - return vscSendTimestampsC0InStore[i].VscId < vscSendTimestampsC0InStore[j].VscId - }) - require.Equal(t, vscSendTimestampsC0InStore, exportedVscSendTimeStampsC0.VscSendTimestamps) - - vscSendTimestampsC1InStore := pk.GetAllVscSendTimestamps(ctx, cChainIDs[1]) - sort.Slice(vscSendTimestampsC1InStore, func(i, j int) bool { - return vscSendTimestampsC1InStore[i].VscId < vscSendTimestampsC1InStore[j].VscId - }) - require.Equal(t, vscSendTimestampsC1InStore, exportedVscSendTimeStampsC1.VscSendTimestamps) } func assertConsumerChainStates(t *testing.T, ctx sdk.Context, pk keeper.Keeper, consumerStates ...providertypes.ConsumerState) { @@ -241,12 +204,6 @@ func assertConsumerChainStates(t *testing.T, ctx sdk.Context, pk keeper.Keeper, require.Equal(t, expVSC, gotVSC) } - for _, ubdOpIdx := range cs.UnbondingOpsIndex { - ubdIndex, found := pk.GetUnbondingOpIndex(ctx, chainID, ubdOpIdx.VscId) - require.True(t, found) - require.Equal(t, ubdOpIdx.UnbondingOpIds, ubdIndex) - } - require.Equal(t, cs.SlashDowntimeAck, pk.GetSlashAcks(ctx, chainID)) } } diff --git a/x/ccv/provider/keeper/grpc_query.go b/x/ccv/provider/keeper/grpc_query.go index d2a36c686d..2635648a6a 100644 --- a/x/ccv/provider/keeper/grpc_query.go +++ b/x/ccv/provider/keeper/grpc_query.go @@ -8,6 +8,7 @@ import ( "google.golang.org/grpc/status" errorsmod "cosmossdk.io/errors" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" @@ -94,6 +95,10 @@ func (k Keeper) GetConsumerChain(ctx sdk.Context, chainID string) (types.Chain, strDenylist[i] = addr.String() } + allowInactiveVals := k.AllowsInactiveValidators(ctx, chainID) + + minStake, _ := k.GetMinStake(ctx, chainID) + return types.Chain{ ChainId: chainID, ClientId: clientID, @@ -103,6 +108,8 @@ func (k Keeper) GetConsumerChain(ctx sdk.Context, chainID string) (types.Chain, ValidatorsPowerCap: validatorsPowerCap, Allowlist: strAllowlist, Denylist: strDenylist, + AllowInactiveVals: allowInactiveVals, + MinStake: minStake, }, nil } @@ -325,14 +332,38 @@ func (k Keeper) QueryConsumerValidators(goCtx context.Context, req *types.QueryC } var validators []*types.QueryConsumerValidatorsValidator - for _, v := range k.GetConsumerValSet(ctx, consumerChainID) { + + consumerValSet, err := k.GetConsumerValSet(ctx, consumerChainID) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + for _, v := range consumerValSet { + + consAddr, err := sdk.ConsAddressFromBech32(sdk.ConsAddress(v.ProviderConsAddr).String()) + if err != nil { + return nil, status.Error(codes.InvalidArgument, "invalid provider address") + } + + var rate math.LegacyDec + consumerRate, found := k.GetConsumerCommissionRate(ctx, consumerChainID, types.NewProviderConsAddress(consAddr)) + if found { + rate = consumerRate + } else { + v, err := k.stakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + if err != nil { + return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("unknown validator: %s", consAddr.String())) + } + rate = v.Commission.Rate + } + validators = append(validators, &types.QueryConsumerValidatorsValidator{ ProviderAddress: sdk.ConsAddress(v.ProviderConsAddr).String(), - ConsumerKey: v.ConsumerPublicKey, + ConsumerKey: v.PublicKey, Power: v.Power, + Rate: rate, }) } - return &types.QueryConsumerValidatorsResponse{ Validators: validators, }, nil @@ -384,7 +415,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, err := k.GetLastBondedValidators(ctx) + activeValidators, err := k.GetLastProviderConsensusActiveValidators(ctx) if err != nil { return false, nil } @@ -392,7 +423,7 @@ func (k Keeper) hasToValidate( // in a Top-N chain, we automatically opt in all validators that belong to the top N minPower, found := k.GetMinimumPowerInTopN(ctx, chainID) if found { - k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) + k.OptInTopNValidators(ctx, chainID, activeValidators, minPower) } else { k.Logger(ctx).Error("did not find min power in top N for chain", "chain", chainID) } @@ -458,33 +489,12 @@ func (k Keeper) QueryValidatorConsumerCommissionRate(goCtx context.Context, req return res, nil } -func (k Keeper) QueryOldestUnconfirmedVsc(goCtx context.Context, req *types.QueryOldestUnconfirmedVscRequest) (*types.QueryOldestUnconfirmedVscResponse, error) { +// QueryBlocksUntilNextEpoch returns the number of blocks until the next epoch +func (k Keeper) QueryBlocksUntilNextEpoch(goCtx context.Context, req *types.QueryBlocksUntilNextEpochRequest) (*types.QueryBlocksUntilNextEpochResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - if req == nil { - return nil, status.Errorf(codes.InvalidArgument, "empty request") - } - - if req.ChainId == "" { - return nil, status.Errorf(codes.InvalidArgument, "invalid request: chain id cannot be empty") - } - - if _, consumerRegistered := k.GetConsumerClientId(ctx, req.ChainId); !consumerRegistered { - return nil, status.Error( - codes.NotFound, - errorsmod.Wrap(types.ErrUnknownConsumerChainId, req.ChainId).Error(), - ) - } - - // Note that GetFirstVscSendTimestamp returns the send timestamp of the oldest - // unconfirmed VSCPacket as these timestamps are deleted when handling VSCMaturedPackets - ts, found := k.GetFirstVscSendTimestamp(ctx, req.ChainId) - if !found { - return nil, status.Error( - codes.NotFound, - errorsmod.Wrap(types.ErrNoUnconfirmedVSCPacket, req.ChainId).Error(), - ) - } + // Calculate the blocks until the next epoch + blocksUntilNextEpoch := k.BlocksUntilNextEpoch(ctx) - return &types.QueryOldestUnconfirmedVscResponse{VscSendTimestamp: ts}, nil + return &types.QueryBlocksUntilNextEpochResponse{BlocksUntilNextEpoch: uint64(blocksUntilNextEpoch)}, nil } diff --git a/x/ccv/provider/keeper/grpc_query_test.go b/x/ccv/provider/keeper/grpc_query_test.go index 3d9f9ee86d..d0ba94174a 100644 --- a/x/ccv/provider/keeper/grpc_query_test.go +++ b/x/ccv/provider/keeper/grpc_query_test.go @@ -3,26 +3,27 @@ package keeper_test import ( "fmt" "testing" - "time" - "cosmossdk.io/math" - "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" + + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - sdktypes "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "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" ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" - "github.com/stretchr/testify/require" ) func TestQueryAllPairsValConAddrByConsumerChainID(t *testing.T) { chainID := consumer - providerConsAddress, err := sdktypes.ConsAddressFromBech32("cosmosvalcons1wpex7anfv3jhystyv3eq20r35a") + providerConsAddress, err := sdk.ConsAddressFromBech32("cosmosvalcons1wpex7anfv3jhystyv3eq20r35a") require.NoError(t, err) providerAddr := types.NewProviderConsAddress(providerConsAddress) @@ -66,49 +67,6 @@ func TestQueryAllPairsValConAddrByConsumerChainID(t *testing.T) { require.Equal(t, &expectedResult, response.PairValConAddr[0]) } -func TestQueryOldestUnconfirmedVsc(t *testing.T) { - chainID := consumer - - pk, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - now := time.Now().UTC() - pk.SetVscSendTimestamp(ctx, chainID, 2, now) - pk.SetVscSendTimestamp(ctx, chainID, 1, now) - pk.SetConsumerClientId(ctx, chainID, "client-1") - - // Request is nil - _, err := pk.QueryOldestUnconfirmedVsc(ctx, nil) - require.Error(t, err) - - // Request with chainId is empty - _, err = pk.QueryOldestUnconfirmedVsc(ctx, &types.QueryOldestUnconfirmedVscRequest{}) - require.Error(t, err) - - // Request with chainId is invalid - _, err = pk.QueryOldestUnconfirmedVsc(ctx, &types.QueryOldestUnconfirmedVscRequest{ChainId: "invalidChainId"}) - require.Error(t, err) - - // Request is valid - response, err := pk.QueryOldestUnconfirmedVsc(ctx, &types.QueryOldestUnconfirmedVscRequest{ChainId: chainID}) - require.NoError(t, err) - expectedResult := types.VscSendTimestamp{ - VscId: 1, - Timestamp: now, - } - require.Equal(t, expectedResult, response.VscSendTimestamp) - - // Make sure that the oldest is queried - pk.DeleteVscSendTimestamp(ctx, chainID, 1) - response, err = pk.QueryOldestUnconfirmedVsc(ctx, &types.QueryOldestUnconfirmedVscRequest{ChainId: chainID}) - require.NoError(t, err) - expectedResult = types.VscSendTimestamp{ - VscId: 2, - Timestamp: now, - } - require.Equal(t, expectedResult, response.VscSendTimestamp) -} - func TestQueryConsumerChainOptedInValidators(t *testing.T) { chainID := "chainID" @@ -141,7 +99,7 @@ func TestQueryConsumerChainOptedInValidators(t *testing.T) { func TestQueryConsumerValidators(t *testing.T) { chainID := "chainID" - pk, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + pk, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() req := types.QueryConsumerValidatorsRequest{ @@ -154,26 +112,40 @@ func TestQueryConsumerValidators(t *testing.T) { providerAddr1 := types.NewProviderConsAddress([]byte("providerAddr1")) consumerKey1 := cryptotestutil.NewCryptoIdentityFromIntSeed(1).TMProtoCryptoPublicKey() - consumerValidator1 := types.ConsumerValidator{ProviderConsAddr: providerAddr1.ToSdkConsAddr(), Power: 1, ConsumerPublicKey: &consumerKey1} + consumerValidator1 := types.ConsensusValidator{ProviderConsAddr: providerAddr1.ToSdkConsAddr(), Power: 1, PublicKey: &consumerKey1} + expectedCommissionRate1 := math.LegacyMustNewDecFromStr("0.123") + pk.SetConsumerCommissionRate(ctx, chainID, providerAddr1, expectedCommissionRate1) providerAddr2 := types.NewProviderConsAddress([]byte("providerAddr2")) consumerKey2 := cryptotestutil.NewCryptoIdentityFromIntSeed(2).TMProtoCryptoPublicKey() - consumerValidator2 := types.ConsumerValidator{ProviderConsAddr: providerAddr2.ToSdkConsAddr(), Power: 2, ConsumerPublicKey: &consumerKey2} + consumerValidator2 := types.ConsensusValidator{ProviderConsAddr: providerAddr2.ToSdkConsAddr(), Power: 2, PublicKey: &consumerKey2} + expectedCommissionRate2 := math.LegacyMustNewDecFromStr("0.123") + pk.SetConsumerCommissionRate(ctx, chainID, providerAddr2, expectedCommissionRate2) expectedResponse := types.QueryConsumerValidatorsResponse{ Validators: []*types.QueryConsumerValidatorsValidator{ - {providerAddr1.String(), &consumerKey1, 1}, - {providerAddr2.String(), &consumerKey2, 2}, + {ProviderAddress: providerAddr1.String(), ConsumerKey: &consumerKey1, Power: 1, Rate: expectedCommissionRate1}, + {ProviderAddress: providerAddr2.String(), ConsumerKey: &consumerKey2, Power: 2, Rate: expectedCommissionRate2}, }, } // 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}) + pk.SetConsumerValSet(ctx, chainID, []types.ConsensusValidator{consumerValidator1, consumerValidator2}) res, err := pk.QueryConsumerValidators(ctx, &req) require.NoError(t, err) require.Equal(t, &expectedResponse, res) + + // validator with no set consumer commission rate + pk.DeleteConsumerCommissionRate(ctx, chainID, providerAddr1) + expectedCommissionRate := math.LegacyMustNewDecFromStr("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, providerAddr1.ToSdkConsAddr()).Return(val, nil).Times(1) + res, _ = pk.QueryConsumerValidators(ctx, &req) + require.Equal(t, expectedCommissionRate, res.Validators[0].Rate) } func TestQueryConsumerChainsValidatorHasToValidate(t *testing.T) { @@ -184,7 +156,7 @@ func TestQueryConsumerChainsValidatorHasToValidate(t *testing.T) { valConsAddr, _ := val.GetConsAddr() providerAddr := types.NewProviderConsAddress(valConsAddr) mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valConsAddr).Return(val, nil).AnyTimes() - testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 1, []stakingtypes.Validator{val}, []int64{1}, -1) // -1 to allow the calls "AnyTimes" + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 1, []stakingtypes.Validator{val}, -1) // -1 to allow the calls "AnyTimes" req := types.QueryConsumerChainsValidatorHasToValidateRequest{ ProviderAddress: providerAddr.String(), @@ -197,10 +169,10 @@ func TestQueryConsumerChainsValidatorHasToValidate(t *testing.T) { } // set `providerAddr` as a consumer validator on "chain1" - pk.SetConsumerValidator(ctx, "chain1", types.ConsumerValidator{ + pk.SetConsumerValidator(ctx, "chain1", types.ConsensusValidator{ ProviderConsAddr: providerAddr.ToSdkConsAddr(), Power: 1, - ConsumerPublicKey: &crypto.PublicKey{ + PublicKey: &crypto.PublicKey{ Sum: &crypto.PublicKey_Ed25519{ Ed25519: []byte{1}, }, @@ -210,6 +182,11 @@ func TestQueryConsumerChainsValidatorHasToValidate(t *testing.T) { // set `providerAddr` as an opted-in validator on "chain3" pk.SetOptedIn(ctx, "chain3", providerAddr) + // set max provider consensus vals to include all validators + params := pk.GetParams(ctx) + params.MaxProviderConsensusValidators = 3 + pk.SetParams(ctx, params) + // `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 expectedChains := []string{"chain1", "chain3"} @@ -270,7 +247,7 @@ func TestGetConsumerChain(t *testing.T) { } powers := []int64{50, 150, 300, 500} // sum = 1000 maxValidators := uint32(180) - testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, maxValidators, vals, powers, -1) // -1 to allow the calls "AnyTimes" + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, maxValidators, vals, -1) // -1 to allow the calls "AnyTimes" for i, val := range vals { valAddr, err := sdk.ValAddressFromBech32(val.GetOperator()) @@ -303,6 +280,15 @@ func TestGetConsumerChain(t *testing.T) { {}, } + allowInactiveVals := []bool{true, false, true, false} + + minStakes := []math.Int{ + math.NewInt(0), + math.NewInt(100), + math.NewInt(200), + math.NewInt(300), + } + expectedGetAllOrder := []types.Chain{} for i, chainID := range chainIDs { clientID := fmt.Sprintf("client-%d", len(chainIDs)-i) @@ -312,6 +298,8 @@ func TestGetConsumerChain(t *testing.T) { pk.SetValidatorSetCap(ctx, chainID, validatorSetCaps[i]) pk.SetValidatorsPowerCap(ctx, chainID, validatorPowerCaps[i]) pk.SetMinimumPowerInTopN(ctx, chainID, expectedMinPowerInTopNs[i]) + pk.SetInactiveValidatorsAllowed(ctx, chainID, allowInactiveVals[i]) + pk.SetMinStake(ctx, chainID, minStakes[i].Uint64()) for _, addr := range allowlists[i] { pk.SetAllowlist(ctx, chainID, addr) } @@ -338,6 +326,8 @@ func TestGetConsumerChain(t *testing.T) { ValidatorsPowerCap: validatorPowerCaps[i], Allowlist: strAllowlist, Denylist: strDenylist, + AllowInactiveVals: allowInactiveVals[i], + MinStake: minStakes[i].Uint64(), }) } diff --git a/x/ccv/provider/keeper/hooks.go b/x/ccv/provider/keeper/hooks.go index 81226a2bdf..3f7f9d21a9 100644 --- a/x/ccv/provider/keeper/hooks.go +++ b/x/ccv/provider/keeper/hooks.go @@ -2,15 +2,12 @@ package keeper import ( "context" - "fmt" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" sdkgov "github.com/cosmos/cosmos-sdk/x/gov/types" - 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" ) @@ -34,105 +31,7 @@ func (k *Keeper) Hooks() Hooks { // staking hooks // -// This stores a record of each unbonding op from staking, allowing us to track which consumer chains have unbonded func (h Hooks) AfterUnbondingInitiated(goCtx context.Context, id uint64) error { - var consumerChainIDS []string - - ctx := sdk.UnwrapSDKContext(goCtx) - - // get validator address from unbonding operation - unbondingType, err := h.k.stakingKeeper.GetUnbondingType(ctx, id) - vadAddrBech32 := "" - if err != nil { - ctx.Logger().Error("undefined type for unbonding operation: id: %d: %s", id, err) - return nil - } - - switch unbondingType { - case stakingtypes.UnbondingType_UnbondingDelegation: - ubd, err := h.k.stakingKeeper.GetUnbondingDelegationByUnbondingID(ctx, id) - if err != nil { - ctx.Logger().Error("unfound ubonding delegation for unbonding id: %d: %s", id, err) - return nil - } - vadAddrBech32 = ubd.ValidatorAddress - case stakingtypes.UnbondingType_Redelegation: - red, err := h.k.stakingKeeper.GetRedelegationByUnbondingID(ctx, id) - if err != nil { - ctx.Logger().Error("unfound relegation for unbonding operation id: %d: %s", id, err) - return nil - } - vadAddrBech32 = red.ValidatorSrcAddress - case stakingtypes.UnbondingType_ValidatorUnbonding: - val, err := h.k.stakingKeeper.GetValidatorByUnbondingID(ctx, id) - if err != nil { - ctx.Logger().Error("unfound validator for unbonding operation id: %d: %s", id, err) - 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, err := h.k.stakingKeeper.GetValidator(ctx, valAddr) - if err != nil { - ctx.Logger().Error("unfound validator for validator address: %s: %s", vadAddrBech32, err) - 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 _, chainID := range h.k.GetAllRegisteredConsumerChainIDs(ctx) { - if h.k.IsConsumerValidator(ctx, chainID, types.NewProviderConsAddress(consAddr)) { - consumerChainIDS = append(consumerChainIDS, chainID) - } - } - - if len(consumerChainIDS) == 0 { - // Do not put the unbonding op on hold if there are no consumer chains - return nil - } - - valsetUpdateID := h.k.GetValidatorSetUpdateId(ctx) - unbondingOp := providertypes.UnbondingOp{ - Id: id, - UnbondingConsumerChains: consumerChainIDS, - } - - // Add to indexes - for _, consumerChainID := range consumerChainIDS { - index, _ := h.k.GetUnbondingOpIndex(ctx, consumerChainID, valsetUpdateID) - index = append(index, id) - h.k.SetUnbondingOpIndex(ctx, consumerChainID, valsetUpdateID, index) - } - - h.k.SetUnbondingOp(ctx, unbondingOp) - - // Call back into staking to tell it to stop this op from unbonding when the unbonding period is complete - if err := h.k.stakingKeeper.PutUnbondingOnHold(ctx, id); err != nil { - // If there was an error putting the unbonding on hold, panic to end execution for - // the current tx and prevent committal of this invalid state. - // - // Note: that in the case of a validator unbonding, AfterUnbondingInitiated is called - // from staking.EndBlock, thus the following panic would halt the chain. - - // In this case PutUnbondingOnHold fails if either the unbonding operation was - // not found or the UnbondingOnHoldRefCount is negative. In either cases, - // the state of the x/staking module of cosmos-sdk is invalid. - panic(fmt.Errorf("unbonding could not be put on hold: %w", err)) - } return nil } @@ -211,8 +110,7 @@ func (h Hooks) BeforeTokenizeShareRecordRemoved(_ context.Context, _ uint64) err // that maps the proposal ID to the consumer chain ID. func (h Hooks) AfterProposalSubmission(goCtx context.Context, proposalID uint64) error { ctx := sdk.UnwrapSDKContext(goCtx) - - if p, ok := h.GetConsumerAdditionLegacyPropFromProp(ctx, proposalID); ok { + if p, ok := h.GetConsumerAdditionFromProp(ctx, proposalID); ok { h.k.SetProposedConsumerChain(ctx, p.ChainId, proposalID) } return nil @@ -225,7 +123,7 @@ func (h Hooks) AfterProposalSubmission(goCtx context.Context, proposalID uint64) func (h Hooks) AfterProposalVotingPeriodEnded(goCtx context.Context, proposalID uint64) error { ctx := sdk.UnwrapSDKContext(goCtx) - if _, ok := h.GetConsumerAdditionLegacyPropFromProp(ctx, proposalID); ok { + if _, ok := h.GetConsumerAdditionFromProp(ctx, proposalID); ok { h.k.DeleteProposedConsumerChainInStore(ctx, proposalID) } return nil @@ -243,9 +141,9 @@ func (h Hooks) AfterProposalFailedMinDeposit(ctx context.Context, proposalID uin return nil } -// GetConsumerAdditionLegacyPropFromProp extracts a consumer addition legacy proposal from +// GetConsumerAdditionFromProp extracts a consumer addition proposal from // the proposal with the given ID -func (h Hooks) GetConsumerAdditionLegacyPropFromProp( +func (h Hooks) GetConsumerAdditionFromProp( ctx sdk.Context, proposalID uint64, ) (providertypes.ConsumerAdditionProposal, bool) { @@ -257,21 +155,35 @@ func (h Hooks) GetConsumerAdditionLegacyPropFromProp( // Iterate over the messages in the proposal // Note that it's assumed that at most ONE message can contain a consumer addition proposal for _, msg := range p.GetMessages() { - sdkMsg, isLegacyProposal := msg.GetCachedValue().(*v1.MsgExecLegacyContent) - if !isLegacyProposal { + sdkMsg, isConsumerAddition := msg.GetCachedValue().(*providertypes.MsgConsumerAddition) + if !isConsumerAddition { continue } - content, err := v1.LegacyContentFromMessage(sdkMsg) - if err != nil { - panic(fmt.Errorf("failed to get legacy proposal %d from prop message", proposalID)) - } - - // returns if legacy prop is of ConsumerAddition proposal type - prop, ok := content.(*providertypes.ConsumerAdditionProposal) - if ok { - return *prop, true + proposal := providertypes.ConsumerAdditionProposal{ + Title: p.Title, + Description: p.Summary, + ChainId: sdkMsg.ChainId, + InitialHeight: sdkMsg.InitialHeight, + GenesisHash: sdkMsg.GenesisHash, + BinaryHash: sdkMsg.BinaryHash, + SpawnTime: sdkMsg.SpawnTime, + UnbondingPeriod: sdkMsg.UnbondingPeriod, + CcvTimeoutPeriod: sdkMsg.CcvTimeoutPeriod, + TransferTimeoutPeriod: sdkMsg.TransferTimeoutPeriod, + ConsumerRedistributionFraction: sdkMsg.ConsumerRedistributionFraction, + BlocksPerDistributionTransmission: sdkMsg.BlocksPerDistributionTransmission, + HistoricalEntries: sdkMsg.HistoricalEntries, + DistributionTransmissionChannel: sdkMsg.DistributionTransmissionChannel, + Top_N: sdkMsg.Top_N, + ValidatorsPowerCap: sdkMsg.ValidatorsPowerCap, + ValidatorSetCap: sdkMsg.ValidatorSetCap, + Allowlist: sdkMsg.Allowlist, + Denylist: sdkMsg.Denylist, + MinStake: sdkMsg.MinStake, + AllowInactiveVals: sdkMsg.AllowInactiveVals, } + return proposal, true } return providertypes.ConsumerAdditionProposal{}, false } diff --git a/x/ccv/provider/keeper/invariants.go b/x/ccv/provider/keeper/invariants.go new file mode 100644 index 0000000000..a6dbf42c1d --- /dev/null +++ b/x/ccv/provider/keeper/invariants.go @@ -0,0 +1,148 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + types "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" +) + +// RegisterInvariants registers all staking invariants +func RegisterInvariants(ir sdk.InvariantRegistry, k *Keeper) { + ir.RegisterRoute(types.ModuleName, "max-provider-validators", + MaxProviderConsensusValidatorsInvariant(k)) + + ir.RegisterRoute(types.ModuleName, "staking-keeper-equivalence", + StakingKeeperEquivalenceInvariant(*k)) +} + +// MaxProviderConsensusValidatorsInvariant checks that the number of provider consensus validators +// is less than or equal to the maximum number of provider consensus validators +func MaxProviderConsensusValidatorsInvariant(k *Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + params := k.GetParams(ctx) + maxProviderConsensusValidators := params.MaxProviderConsensusValidators + + consensusValidators, err := k.GetLastProviderConsensusValSet(ctx) + if err != nil { + panic(fmt.Errorf("failed to get last provider consensus validator set: %w", err)) + } + if int64(len(consensusValidators)) > maxProviderConsensusValidators { + return sdk.FormatInvariant(types.ModuleName, "max-provider-validators", + fmt.Sprintf("number of provider consensus validators: %d, exceeds max: %d", + len(consensusValidators), maxProviderConsensusValidators)), true + } + + return "", false + } +} + +// StakingKeeperEquivalenceInvariant checks that *if* MaxProviderConsensusValidators == MaxValidators, then +// the staking keeper and the provider keeper +// return the same values for their common interface, +// i.e. the functions from staking_keeper_interface.go +func StakingKeeperEquivalenceInvariant(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + maxProviderConsensusValidators := k.GetParams(ctx).MaxProviderConsensusValidators + if maxProviderConsensusValidators == 0 { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("maxProviderConsensusVals is 0: %v", maxProviderConsensusValidators)), true + } + stakingKeeper := k.stakingKeeper + + maxValidators, err := stakingKeeper.MaxValidators(ctx) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("error getting max validators from staking keeper: %v", err)), true + } + + if maxValidators != uint32(maxProviderConsensusValidators) { + // the invariant should only check something if these two numbers are equal, + // so skip in this case + k.Logger(ctx).Info("skipping staking keeper equivalence invariant check because max validators is equal to max provider consensus validators") + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("maxValidators: %v, maxProviderVals: %v", maxValidators, maxProviderConsensusValidators)), false + } + + // check that the staking keeper and the provider keeper return the same values + // for the common interface functions + + // Check IterateBondedValidatorsByPower + providerBondedValidators := make([]stakingtypes.Validator, 0) + err = k.IterateBondedValidatorsByPower(ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) { + providerBondedValidators = append(providerBondedValidators, validator.(stakingtypes.Validator)) + return false + }) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("error getting provider bonded validators: %v", err)), true + } + + stakingBondedValidators := make([]stakingtypes.Validator, 0) + err = stakingKeeper.IterateBondedValidatorsByPower(ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) { + stakingBondedValidators = append(stakingBondedValidators, validator.(stakingtypes.Validator)) + return false + }) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("error getting staking bonded validators: %v", err)), true + } + + if len(providerBondedValidators) != len(stakingBondedValidators) { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("provider bonded validators: %v, staking bonded validators: %v", + providerBondedValidators, stakingBondedValidators)), true + } + + for i, providerVal := range providerBondedValidators { + stakingVal := stakingBondedValidators[i] + + if !providerVal.Equal(&stakingVal) { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("provider validator: %v, staking validator: %v", + providerVal, stakingVal)), true + } + } + + // Check TotalBondedTokens + providerTotalBondedTokens, err := k.TotalBondedTokens(ctx) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("error getting provider total bonded tokens: %v", err)), true + } + + stakingTotalBondedTokens, err := stakingKeeper.TotalBondedTokens(ctx) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("error getting staking total bonded tokens: %v", err)), true + } + + if !providerTotalBondedTokens.Equal(stakingTotalBondedTokens) { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("provider total bonded tokens: %v, staking total bonded tokens: %v", + providerTotalBondedTokens, stakingTotalBondedTokens)), true + } + + // Check BondedRatio + providerBondedRatio, err := k.BondedRatio(ctx) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("error getting provider bonded ratio: %v", err)), true + } + + stakingBondedRatio, err := stakingKeeper.BondedRatio(ctx) + if err != nil { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("error getting staking bonded ratio: %v", err)), true + } + + if !providerBondedRatio.Equal(stakingBondedRatio) { + return sdk.FormatInvariant(types.ModuleName, "staking-keeper-equivalence", + fmt.Sprintf("provider bonded ratio: %v, staking bonded ratio: %v", + providerBondedRatio, stakingBondedRatio)), true + } + + return "", false + } +} diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 22f5f33626..1125ae52c8 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -143,6 +143,10 @@ func (k Keeper) mustValidateFields() { // ccv.PanicIfZeroOrNil(k.govKeeper, "govKeeper") // 17 } +func (k *Keeper) SetGovKeeper(govKeeper govkeeper.Keeper) { + k.govKeeper = govKeeper +} + // Logger returns a module-specific logger. func (k Keeper) Logger(ctx context.Context) log.Logger { sdkCtx := sdk.UnwrapSDKContext(ctx) @@ -235,12 +239,12 @@ func (k Keeper) DeleteProposedConsumerChainInStore(ctx sdk.Context, proposalID u // GetAllProposedConsumerChainIDs returns the proposed chainID of all gov consumerAddition proposals that are still in the voting period. func (k Keeper) GetAllProposedConsumerChainIDs(ctx sdk.Context) []types.ProposedChain { store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.ProposedConsumerChainByteKey}) + iterator := storetypes.KVStorePrefixIterator(store, types.ProposedConsumerChainKeyPrefix()) defer iterator.Close() proposedChains := []types.ProposedChain{} for ; iterator.Valid(); iterator.Next() { - proposalID, err := types.ParseProposedConsumerChainKey(types.ProposedConsumerChainByteKey, iterator.Key()) + proposalID, err := types.ParseProposedConsumerChainKey(iterator.Key()) if err != nil { panic(fmt.Errorf("proposed chains cannot be parsed: %w", err)) } @@ -270,13 +274,13 @@ func (k Keeper) GetAllPendingConsumerChainIDs(ctx sdk.Context) []string { // 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 +// ChainToClientKeyPrefix | chainID // Thus, the returned array is in ascending order of chainIDs. func (k Keeper) GetAllRegisteredConsumerChainIDs(ctx sdk.Context) []string { chainIDs := []string{} store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.ChainToClientBytePrefix}) + iterator := storetypes.KVStorePrefixIterator(store, types.ChainToClientKeyPrefix()) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { @@ -315,11 +319,11 @@ func (k Keeper) DeleteChannelToChain(ctx sdk.Context, channelID string) { // // Note that mapping from CCV channel IDs to consumer chainIDs // is stored under keys with the following format: -// ChannelToChainBytePrefix | channelID +// ChannelToChainKeyPrefix | channelID // Thus, the returned array is in ascending order of channelIDs. func (k Keeper) GetAllChannelToChains(ctx sdk.Context) (channels []types.ChannelToChain) { store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.ChannelToChainBytePrefix}) + iterator := storetypes.KVStorePrefixIterator(store, types.ChannelToChainKeyPrefix()) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { @@ -426,8 +430,6 @@ func (k Keeper) SetConsumerChain(ctx sdk.Context, channelID string) error { k.SetChannelToChain(ctx, channelID, chainID) // - set current block height for the consumer chain initialization k.SetInitChainHeight(ctx, chainID, uint64(ctx.BlockHeight())) - // - remove init timeout timestamp - k.DeleteInitTimeoutTimestamp(ctx, chainID) // emit event on successful addition ctx.EventManager().EmitEvent( @@ -443,260 +445,6 @@ func (k Keeper) SetConsumerChain(ctx sdk.Context, channelID string) error { return nil } -// SetUnbondingOp sets the UnbondingOp by its unique ID -func (k Keeper) SetUnbondingOp(ctx sdk.Context, unbondingOp types.UnbondingOp) { - store := ctx.KVStore(k.storeKey) - bz, err := unbondingOp.Marshal() - if err != nil { - // An error here would indicate something is very wrong, - // unbondingOp is either instantiated in AfterUnbondingInitiated, - // updated correctly by RemoveConsumerFromUnbondingOp, - // or set during InitGenesis. - panic(fmt.Errorf("unbonding op could not be marshaled: %w", err)) - } - store.Set(types.UnbondingOpKey(unbondingOp.Id), bz) -} - -// GetUnbondingOp gets a UnbondingOp by its unique ID -func (k Keeper) GetUnbondingOp(ctx sdk.Context, id uint64) (types.UnbondingOp, bool) { - store := ctx.KVStore(k.storeKey) - bz := store.Get(types.UnbondingOpKey(id)) - if bz == nil { - return types.UnbondingOp{}, false - } - - var unbondingOp types.UnbondingOp - if err := unbondingOp.Unmarshal(bz); err != nil { - // An error here would indicate something is very wrong, - // the UnbondingOp is assumed to be correctly serialized in SetUnbondingOp. - panic(fmt.Errorf("failed to unmarshal UnbondingOp: %w", err)) - } - - return unbondingOp, true -} - -// DeleteUnbondingOp deletes a UnbondingOp given its ID -func (k Keeper) DeleteUnbondingOp(ctx sdk.Context, id uint64) { - store := ctx.KVStore(k.storeKey) - store.Delete(types.UnbondingOpKey(id)) -} - -// GetAllUnbondingOps gets all UnbondingOps, where each UnbondingOp consists -// of its unique ID and a list of consumer chainIDs that the unbonding operation -// is waiting on. -// -// Note that UnbondingOps are stored under keys with the following format: -// UnbondingOpBytePrefix | ID -// Thus, the iteration is in ascending order of IDs. -func (k Keeper) GetAllUnbondingOps(ctx sdk.Context) (ops []types.UnbondingOp) { - store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.UnbondingOpBytePrefix}) - - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - id := binary.BigEndian.Uint64(iterator.Key()[1:]) - bz := iterator.Value() - if bz == nil { - // An error here would indicate something is very wrong, - // the UnbondingOp is assumed to be correctly set in SetUnbondingOp. - panic(fmt.Errorf("unbonding operation is nil for id %d", id)) - } - var unbondingOp types.UnbondingOp - if err := unbondingOp.Unmarshal(bz); err != nil { - // An error here would indicate something is very wrong, - // the UnbondingOp is assumed to be correctly serialized in SetUnbondingOp. - panic(fmt.Errorf("failed to unmarshal UnbondingOp: %w", err)) - } - - ops = append(ops, unbondingOp) - } - - return ops -} - -// RemoveConsumerFromUnbondingOp removes a consumer chain ID that the unbonding op with 'id' is waiting on. -// The method returns true if the unbonding op can complete. In this case the record is removed from store. -// The method panics if the unbonding op with 'id' is not found. -func (k Keeper) RemoveConsumerFromUnbondingOp(ctx sdk.Context, id uint64, chainID string) (canComplete bool) { - // Get the unbonding op from store - unbondingOp, found := k.GetUnbondingOp(ctx, id) - if !found { - panic(fmt.Errorf("internal state corrupted; could not find UnbondingOp with ID %d", id)) - } - - // Remove consumer chain ID from unbonding op - var numRemoved int - unbondingOp.UnbondingConsumerChains, numRemoved = removeStringFromSlice(unbondingOp.UnbondingConsumerChains, chainID) - if numRemoved > 0 { - k.Logger(ctx).Debug("unbonding operation matured on consumer", "chainID", chainID, "opID", id) - - if len(unbondingOp.UnbondingConsumerChains) == 0 { - // Delete unbonding op - k.DeleteUnbondingOp(ctx, id) - // No more consumer chains; the unbonding op can complete - canComplete = true - } else { - // Update unbonding op in store - k.SetUnbondingOp(ctx, unbondingOp) - } - } - return -} - -func removeStringFromSlice(slice []string, x string) (newSlice []string, numRemoved int) { - for _, y := range slice { - if x != y { - newSlice = append(newSlice, y) - } - } - - return newSlice, len(slice) - len(newSlice) -} - -// SetUnbondingOpIndex sets the IDs of unbonding operations that are waiting for -// a VSCMaturedPacket with vscID from a consumer with chainID -func (k Keeper) SetUnbondingOpIndex(ctx sdk.Context, chainID string, vscID uint64, ids []uint64) { - store := ctx.KVStore(k.storeKey) - - vscUnbondingOps := types.VscUnbondingOps{ - VscId: vscID, - UnbondingOpIds: ids, - } - bz, err := vscUnbondingOps.Marshal() - if err != nil { - // An error here would indicate something is very wrong, - // vscUnbondingOps is instantiated in this method and should be able to be marshaled. - panic(fmt.Errorf("failed to marshal VscUnbondingOps: %w", err)) - } - - store.Set(types.UnbondingOpIndexKey(chainID, vscID), bz) -} - -// GetAllUnbondingOpIndexes gets all unbonding indexes for a given chain id, -// i.e., all the IDs of unbonding operations that are waiting for -// VSCMaturedPackets from a consumer with chainID. -// -// Note that the unbonding indexes for a given chainID are stored under keys with the following format: -// UnbondingOpIndexBytePrefix | len(chainID) | chainID | vscID -// Thus, the returned array is in ascending order of vscIDs. -func (k Keeper) GetAllUnbondingOpIndexes(ctx sdk.Context, chainID string) (indexes []types.VscUnbondingOps) { - store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.UnbondingOpIndexBytePrefix, chainID)) - defer iterator.Close() - - for ; iterator.Valid(); iterator.Next() { - var vscUnbondingOps types.VscUnbondingOps - if err := vscUnbondingOps.Unmarshal(iterator.Value()); err != nil { - // An error here would indicate something is very wrong, - // the VscUnbondingOps are assumed to be correctly serialized in SetUnbondingOpIndex. - panic(fmt.Errorf("failed to unmarshal VscUnbondingOps: %w", err)) - } - - indexes = append(indexes, types.VscUnbondingOps{ - VscId: vscUnbondingOps.GetVscId(), - UnbondingOpIds: vscUnbondingOps.GetUnbondingOpIds(), - }) - } - - return indexes -} - -// GetUnbondingOpIndex gets the IDs of unbonding operations that are waiting for -// a VSCMaturedPacket with vscID from a consumer with chainID -func (k Keeper) GetUnbondingOpIndex(ctx sdk.Context, chainID string, vscID uint64) ([]uint64, bool) { - store := ctx.KVStore(k.storeKey) - - bz := store.Get(types.UnbondingOpIndexKey(chainID, vscID)) - if bz == nil { - return []uint64{}, false - } - - var vscUnbondingOps types.VscUnbondingOps - if err := vscUnbondingOps.Unmarshal(bz); err != nil { - // An error here would indicate something is very wrong, - // the VscUnbondingOps are assumed to be correctly serialized in SetUnbondingOpIndex. - panic(fmt.Errorf("failed to unmarshal VscUnbondingOps: %w", err)) - } - - return vscUnbondingOps.GetUnbondingOpIds(), true -} - -// DeleteUnbondingOpIndex deletes the IDs of unbonding operations that are waiting for -// a VSCMaturedPacket with vscID from a consumer with chainID -func (k Keeper) DeleteUnbondingOpIndex(ctx sdk.Context, chainID string, vscID uint64) { - store := ctx.KVStore(k.storeKey) - store.Delete(types.UnbondingOpIndexKey(chainID, vscID)) -} - -// GetUnbondingOpsFromIndex gets the unbonding ops waiting for a given chainID and vscID -func (k Keeper) GetUnbondingOpsFromIndex(ctx sdk.Context, chainID string, valsetUpdateID uint64) (entries []types.UnbondingOp) { - ids, found := k.GetUnbondingOpIndex(ctx, chainID, valsetUpdateID) - if !found { - return entries - } - for _, id := range ids { - entry, found := k.GetUnbondingOp(ctx, id) - if !found { - // An error here would indicate something is very wrong. - // Every UnbondingOpIndex is assumed to have the corresponding UnbondingOps set in store. - // This is done in AfterUnbondingInitiated and InitGenesis. - panic("did not find UnbondingOp according to index- index was probably not correctly updated") - } - entries = append(entries, entry) - } - - return entries -} - -// GetMaturedUnbondingOps returns the list of matured unbonding operation ids -func (k Keeper) GetMaturedUnbondingOps(ctx sdk.Context) (ids []uint64) { - store := ctx.KVStore(k.storeKey) - bz := store.Get(types.MaturedUnbondingOpsKey()) - if bz == nil { - // Note that every call to ConsumeMaturedUnbondingOps - // deletes the MaturedUnbondingOpsKey, which means that - // the first call to GetMaturedUnbondingOps after that - // will enter this branch. - return nil - } - - var ops types.MaturedUnbondingOps - if err := ops.Unmarshal(bz); err != nil { - // An error here would indicate something is very wrong, - // the MaturedUnbondingOps are assumed to be correctly serialized in AppendMaturedUnbondingOps. - panic(fmt.Errorf("failed to unmarshal MaturedUnbondingOps: %w", err)) - } - return ops.GetIds() -} - -// AppendMaturedUnbondingOps adds a list of ids to the list of matured unbonding operation ids -func (k Keeper) AppendMaturedUnbondingOps(ctx sdk.Context, ids []uint64) { - if len(ids) == 0 { - return - } - existingIds := k.GetMaturedUnbondingOps(ctx) - maturedOps := types.MaturedUnbondingOps{ - Ids: append(existingIds, ids...), - } - - store := ctx.KVStore(k.storeKey) - bz, err := maturedOps.Marshal() - if err != nil { - // An error here would indicate something is very wrong, - // maturedOps is instantiated in this method and should be able to be marshaled. - panic(fmt.Sprintf("failed to marshal matured unbonding operations: %s", err)) - } - store.Set(types.MaturedUnbondingOpsKey(), bz) -} - -// ConsumeMaturedUnbondingOps empties and returns list of matured unbonding operation ids (if it exists) -func (k Keeper) ConsumeMaturedUnbondingOps(ctx sdk.Context) []uint64 { - ids := k.GetMaturedUnbondingOps(ctx) - store := ctx.KVStore(k.storeKey) - store.Delete(types.MaturedUnbondingOpsKey()) - return ids -} - // Retrieves the underlying client state corresponding to a connection ID. func (k Keeper) getUnderlyingClient(ctx sdk.Context, connectionID string) ( clientID string, tmClient *ibctmtypes.ClientState, err error, @@ -776,11 +524,11 @@ func (k Keeper) GetValsetUpdateBlockHeight(ctx sdk.Context, valsetUpdateId uint6 // GetAllValsetUpdateBlockHeights gets all the block heights for all valset updates // // Note that the mapping from vscIDs to block heights is stored under keys with the following format: -// ValsetUpdateBlockHeightBytePrefix | vscID +// ValsetUpdateBlockHeightKeyPrefix | vscID // Thus, the returned array is in ascending order of vscIDs. func (k Keeper) GetAllValsetUpdateBlockHeights(ctx sdk.Context) (valsetUpdateBlockHeights []types.ValsetUpdateIdToHeight) { store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.ValsetUpdateBlockHeightBytePrefix}) + iterator := storetypes.KVStorePrefixIterator(store, types.ValsetUpdateBlockHeightKeyPrefix()) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { @@ -954,175 +702,6 @@ func (k Keeper) DeleteConsumerClientId(ctx sdk.Context, chainID string) { store.Delete(types.ChainToClientKey(chainID)) } -// SetInitTimeoutTimestamp sets the init timeout timestamp for the given chain ID -func (k Keeper) SetInitTimeoutTimestamp(ctx sdk.Context, chainID string, ts uint64) { - store := ctx.KVStore(k.storeKey) - tsBytes := make([]byte, 8) - binary.BigEndian.PutUint64(tsBytes, ts) - store.Set(types.InitTimeoutTimestampKey(chainID), tsBytes) -} - -// GetInitTimeoutTimestamp returns the init timeout timestamp for the given chain ID. -// This method is used only in testing. -func (k Keeper) GetInitTimeoutTimestamp(ctx sdk.Context, chainID string) (uint64, bool) { - store := ctx.KVStore(k.storeKey) - bz := store.Get(types.InitTimeoutTimestampKey(chainID)) - if bz == nil { - return 0, false - } - return binary.BigEndian.Uint64(bz), true -} - -// DeleteInitTimeoutTimestamp removes from the store the init timeout timestamp for the given chainID. -func (k Keeper) DeleteInitTimeoutTimestamp(ctx sdk.Context, chainID string) { - store := ctx.KVStore(k.storeKey) - store.Delete(types.InitTimeoutTimestampKey(chainID)) -} - -// GetAllInitTimeoutTimestamps gets all init timeout timestamps in the store. -// -// Note that the init timeout timestamps are stored under keys with the following format: -// InitTimeoutTimestampBytePrefix | chainID -// Thus, the returned array is in ascending order of chainIDs (NOT in timestamp order). -func (k Keeper) GetAllInitTimeoutTimestamps(ctx sdk.Context) (initTimeoutTimestamps []types.InitTimeoutTimestamp) { - store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.InitTimeoutTimestampBytePrefix}) - - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - chainID := string(iterator.Key()[1:]) - ts := binary.BigEndian.Uint64(iterator.Value()) - - initTimeoutTimestamps = append(initTimeoutTimestamps, types.InitTimeoutTimestamp{ - ChainId: chainID, - Timestamp: ts, - }) - } - - return initTimeoutTimestamps -} - -// SetVscSendTimestamp sets the VSC send timestamp -// for a VSCPacket with ID vscID sent to a chain with ID chainID -func (k Keeper) SetVscSendTimestamp( - ctx sdk.Context, - chainID string, - vscID uint64, - timestamp time.Time, -) { - store := ctx.KVStore(k.storeKey) - - // Convert timestamp into bytes for storage - timeBz := sdk.FormatTimeBytes(timestamp) - - store.Set(types.VscSendingTimestampKey(chainID, vscID), timeBz) -} - -// GetVscSendTimestamp returns a VSC send timestamp by chainID and vscID -// -// Note: This method is used only for testing. -func (k Keeper) GetVscSendTimestamp(ctx sdk.Context, chainID string, vscID uint64) (time.Time, bool) { - store := ctx.KVStore(k.storeKey) - - timeBz := store.Get(types.VscSendingTimestampKey(chainID, vscID)) - if timeBz == nil { - return time.Time{}, false - } - - ts, err := sdk.ParseTimeBytes(timeBz) - if err != nil { - return time.Time{}, false - } - return ts, true -} - -// DeleteVscSendTimestamp removes from the store a specific VSC send timestamp -// for the given chainID and vscID. -func (k Keeper) DeleteVscSendTimestamp(ctx sdk.Context, chainID string, vscID uint64) { - store := ctx.KVStore(k.storeKey) - store.Delete(types.VscSendingTimestampKey(chainID, vscID)) -} - -// GetAllVscSendTimestamps gets an array of all the vsc send timestamps of the given chainID. -// -// Note that the vsc send timestamps of a given chainID are stored under keys with the following format: -// VscSendTimestampBytePrefix | len(chainID) | chainID | vscID -// Thus, the iteration is in ascending order of vscIDs, and as a result in send timestamp order. -func (k Keeper) GetAllVscSendTimestamps(ctx sdk.Context, chainID string) (vscSendTimestamps []types.VscSendTimestamp) { - store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.VscSendTimestampBytePrefix, chainID)) - defer iterator.Close() - - for ; iterator.Valid(); iterator.Next() { - _, vscID, err := types.ParseVscSendingTimestampKey(iterator.Key()) - if err != nil { - // An error here would indicate something is very wrong, - // the store key is assumed to be correctly serialized in SetVscSendTimestamp. - panic(fmt.Errorf("failed to parse VscSendTimestampKey: %w", err)) - } - ts, err := sdk.ParseTimeBytes(iterator.Value()) - if err != nil { - // An error here would indicate something is very wrong, - // the timestamp is assumed to be correctly serialized in SetVscSendTimestamp. - panic(fmt.Errorf("failed to parse timestamp value: %w", err)) - } - - vscSendTimestamps = append(vscSendTimestamps, types.VscSendTimestamp{ - VscId: vscID, - Timestamp: ts, - }) - } - - return vscSendTimestamps -} - -// DeleteVscSendTimestampsForConsumer deletes all VSC send timestamps for a given consumer chain -func (k Keeper) DeleteVscSendTimestampsForConsumer(ctx sdk.Context, consumerChainID string) { - store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.VscSendTimestampBytePrefix, consumerChainID)) - - defer iterator.Close() - - keysToDel := [][]byte{} - for ; iterator.Valid(); iterator.Next() { - keysToDel = append(keysToDel, iterator.Key()) - } - - // Delete data for this consumer - for _, key := range keysToDel { - store.Delete(key) - } -} - -// GetFirstVscSendTimestamp gets the vsc send timestamp with the lowest vscID for the given chainID. -func (k Keeper) GetFirstVscSendTimestamp(ctx sdk.Context, chainID string) (vscSendTimestamp types.VscSendTimestamp, found bool) { - store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.VscSendTimestampBytePrefix, chainID)) - defer iterator.Close() - - if iterator.Valid() { - _, vscID, err := types.ParseVscSendingTimestampKey(iterator.Key()) - if err != nil { - // An error here would indicate something is very wrong, - // the store key is assumed to be correctly serialized in SetVscSendTimestamp. - panic(fmt.Errorf("failed to parse VscSendTimestampKey: %w", err)) - } - ts, err := sdk.ParseTimeBytes(iterator.Value()) - if err != nil { - // An error here would indicate something is very wrong, - // the timestamp is assumed to be correctly serialized in SetVscSendTimestamp. - panic(fmt.Errorf("failed to parse timestamp value: %w", err)) - } - - return types.VscSendTimestamp{ - VscId: vscID, - Timestamp: ts, - }, true - } - - return types.VscSendTimestamp{}, false -} - // SetSlashLog updates validator's slash log for a consumer chain // If an entry exists for a given validator address, at least one // double signing slash packet was received by the provider from at least one consumer chain @@ -1243,7 +822,7 @@ func (k Keeper) GetAllOptedIn( chainID string, ) (providerConsAddresses []types.ProviderConsAddress) { store := ctx.KVStore(k.storeKey) - key := types.ChainIdWithLenKey(types.OptedInBytePrefix, chainID) + key := types.ChainIdWithLenKey(types.OptedInKeyPrefix(), chainID) iterator := storetypes.KVStorePrefixIterator(store, key) defer iterator.Close() @@ -1260,7 +839,7 @@ func (k Keeper) DeleteAllOptedIn( chainID string, ) { store := ctx.KVStore(k.storeKey) - key := types.ChainIdWithLenKey(types.OptedInBytePrefix, chainID) + key := types.ChainIdWithLenKey(types.OptedInKeyPrefix(), chainID) iterator := storetypes.KVStorePrefixIterator(store, key) var keysToDel [][]byte @@ -1323,7 +902,7 @@ func (k Keeper) GetAllCommissionRateValidators( chainID string, ) (addresses []types.ProviderConsAddress) { store := ctx.KVStore(k.storeKey) - key := types.ChainIdWithLenKey(types.ConsumerCommissionRatePrefix, chainID) + key := types.ChainIdWithLenKey(types.ConsumerCommissionRateKeyPrefix(), chainID) iterator := storetypes.KVStorePrefixIterator(store, key) defer iterator.Close() @@ -1425,7 +1004,7 @@ func (k Keeper) SetAllowlist( providerAddr types.ProviderConsAddress, ) { store := ctx.KVStore(k.storeKey) - store.Set(types.AllowlistCapKey(chainID, providerAddr), []byte{}) + store.Set(types.AllowlistKey(chainID, providerAddr), []byte{}) } // GetAllowList returns all allowlisted validators @@ -1434,7 +1013,7 @@ func (k Keeper) GetAllowList( chainID string, ) (providerConsAddresses []types.ProviderConsAddress) { store := ctx.KVStore(k.storeKey) - key := types.ChainIdWithLenKey(types.AllowlistPrefix, chainID) + key := types.ChainIdWithLenKey(types.AllowlistKeyPrefix(), chainID) iterator := storetypes.KVStorePrefixIterator(store, key) defer iterator.Close() @@ -1452,14 +1031,14 @@ func (k Keeper) IsAllowlisted( providerAddr types.ProviderConsAddress, ) bool { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.AllowlistCapKey(chainID, providerAddr)) + bz := store.Get(types.AllowlistKey(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 := storetypes.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.AllowlistPrefix, chainID)) + iterator := storetypes.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.AllowlistKeyPrefix(), chainID)) defer iterator.Close() keysToDel := [][]byte{} @@ -1475,7 +1054,7 @@ func (k Keeper) DeleteAllowlist(ctx sdk.Context, chainID string) { // 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 := storetypes.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.AllowlistPrefix, chainID)) + iterator := storetypes.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.AllowlistKeyPrefix(), chainID)) defer iterator.Close() return !iterator.Valid() @@ -1488,7 +1067,7 @@ func (k Keeper) SetDenylist( providerAddr types.ProviderConsAddress, ) { store := ctx.KVStore(k.storeKey) - store.Set(types.DenylistCapKey(chainID, providerAddr), []byte{}) + store.Set(types.DenylistKey(chainID, providerAddr), []byte{}) } // GetDenyList returns all denylisted validators @@ -1497,7 +1076,7 @@ func (k Keeper) GetDenyList( chainID string, ) (providerConsAddresses []types.ProviderConsAddress) { store := ctx.KVStore(k.storeKey) - key := types.ChainIdWithLenKey(types.DenylistPrefix, chainID) + key := types.ChainIdWithLenKey(types.DenylistKeyPrefix(), chainID) iterator := storetypes.KVStorePrefixIterator(store, key) defer iterator.Close() @@ -1515,14 +1094,14 @@ func (k Keeper) IsDenylisted( providerAddr types.ProviderConsAddress, ) bool { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.DenylistCapKey(chainID, providerAddr)) + bz := store.Get(types.DenylistKey(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 := storetypes.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.DenylistPrefix, chainID)) + iterator := storetypes.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.DenylistKeyPrefix(), chainID)) defer iterator.Close() keysToDel := [][]byte{} @@ -1538,7 +1117,7 @@ func (k Keeper) DeleteDenylist(ctx sdk.Context, chainID string) { // 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 := storetypes.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.DenylistPrefix, chainID)) + iterator := storetypes.KVStorePrefixIterator(store, types.ChainIdWithLenKey(types.DenylistKeyPrefix(), chainID)) defer iterator.Close() return !iterator.Valid() @@ -1582,3 +1161,94 @@ func (k Keeper) DeleteMinimumPowerInTopN( store := ctx.KVStore(k.storeKey) store.Delete(types.MinimumPowerInTopNKey(chainID)) } + +// SetMinStake sets the minimum stake required for a validator to validate +// a given consumer chain. +func (k Keeper) SetMinStake( + ctx sdk.Context, + chainID string, + minStake uint64, +) { + store := ctx.KVStore(k.storeKey) + + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, minStake) + + store.Set(types.MinStakeKey(chainID), buf) +} + +// GetMinStake returns the minimum stake required for a validator to validate +// a given consumer chain. +func (k Keeper) GetMinStake( + ctx sdk.Context, + chainID string, +) (uint64, bool) { + store := ctx.KVStore(k.storeKey) + buf := store.Get(types.MinStakeKey(chainID)) + if buf == nil { + return 0, false + } + return binary.BigEndian.Uint64(buf), true +} + +// DeleteMinStake removes the minimum stake required for a validator to validate +// a given consumer chain. +func (k Keeper) DeleteMinStake( + ctx sdk.Context, + chainID string, +) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.MinStakeKey(chainID)) +} + +// SetInactiveValidatorsAllowed sets whether inactive validators are allowed to validate +// a given consumer chain. +func (k Keeper) SetInactiveValidatorsAllowed( + ctx sdk.Context, + chainID string, + allowed bool, +) { + if allowed { + k.EnableInactiveValidators(ctx, chainID) + } else { + k.DisableInactiveValidators(ctx, chainID) + } +} + +// EnableInactiveValidators sets the flag to signal that inactive validators are allowed to validate +// a given consumer chain. +func (k Keeper) EnableInactiveValidators( + ctx sdk.Context, + chainID string, +) { + store := ctx.KVStore(k.storeKey) + store.Set(types.AllowInactiveValidatorsKey(chainID), []byte{}) +} + +// AllowsInactiveValidators returns whether inactive validators are allowed to validate +// a given consumer chain. +func (k Keeper) AllowsInactiveValidators( + ctx sdk.Context, + chainID string, +) bool { + store := ctx.KVStore(k.storeKey) + return store.Has(types.AllowInactiveValidatorsKey(chainID)) +} + +// DisableInactiveValidators removes the flag of whether inactive validators are allowed to validate +// a given consumer chain. +func (k Keeper) DisableInactiveValidators( + ctx sdk.Context, + chainID string, +) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.AllowInactiveValidatorsKey(chainID)) +} + +func (k Keeper) UnbondingCanComplete(ctx sdk.Context, id uint64) error { + return k.stakingKeeper.UnbondingCanComplete(ctx, id) +} + +func (k Keeper) UnbondingTime(ctx sdk.Context) (time.Duration, error) { + return k.stakingKeeper.UnbondingTime(ctx) +} diff --git a/x/ccv/provider/keeper/keeper_test.go b/x/ccv/provider/keeper/keeper_test.go index e61a93cda1..442e86c09d 100644 --- a/x/ccv/provider/keeper/keeper_test.go +++ b/x/ccv/provider/keeper/keeper_test.go @@ -5,13 +5,13 @@ import ( "fmt" "sort" "testing" - "time" "cosmossdk.io/math" ibctesting "github.com/cosmos/ibc-go/v8/testing" "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" @@ -224,175 +224,6 @@ func TestInitHeight(t *testing.T) { } } -// TestGetAllUnbondingOpIndexes tests GetAllUnbondingOpIndexes behavior correctness -func TestGetAllUnbondingOpIndexes(t *testing.T) { - pk, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - ops := []types.VscUnbondingOps{ - { - VscId: 2, - UnbondingOpIds: []uint64{4, 5, 6, 7}, - }, - { - VscId: 1, - UnbondingOpIds: []uint64{1, 2, 3}, - }, - { - VscId: 4, - UnbondingOpIds: []uint64{10}, - }, - { - VscId: 3, - UnbondingOpIds: []uint64{8, 9}, - }, - } - // sorting by CrossChainValidator.Address - expectedGetAllOrder := ops - sort.Slice(expectedGetAllOrder, func(i, j int) bool { - return expectedGetAllOrder[i].VscId < expectedGetAllOrder[j].VscId - }) - - pk.SetUnbondingOpIndex(ctx, "chain-2", 1, []uint64{1, 2, 3}) - for _, op := range ops { - pk.SetUnbondingOpIndex(ctx, "chain-1", op.VscId, op.UnbondingOpIds) - } - - // iterate and check all results are returned in the expected order - result := pk.GetAllUnbondingOpIndexes(ctx, "chain-1") - require.Len(t, result, len(ops)) - require.Equal(t, result, expectedGetAllOrder) -} - -func TestMaturedUnbondingOps(t *testing.T) { - providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - ids := providerKeeper.GetMaturedUnbondingOps(ctx) - require.Nil(t, ids) - - unbondingOpIds := []uint64{0, 1, 2, 3, 4, 5, 6} - providerKeeper.AppendMaturedUnbondingOps(ctx, unbondingOpIds) - - ids = providerKeeper.ConsumeMaturedUnbondingOps(ctx) - require.Equal(t, len(unbondingOpIds), len(ids)) - for i := 0; i < len(unbondingOpIds); i++ { - require.Equal(t, unbondingOpIds[i], ids[i]) - } -} - -func TestInitTimeoutTimestamp(t *testing.T) { - pk, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - now := time.Now().UTC() - nsNow := uint64(now.UnixNano()) - timeoutTimestamps := []types.InitTimeoutTimestamp{ - { - ChainId: "chain-2", - Timestamp: nsNow, - }, - { - ChainId: "chain-1", - Timestamp: nsNow + 10, - }, - { - ChainId: "chain-4", - Timestamp: nsNow - 10, - }, - { - ChainId: "chain-3", - Timestamp: nsNow, - }, - } - - expectedGetAllOrder := timeoutTimestamps - // sorting by ChainId - sort.Slice(expectedGetAllOrder, func(i, j int) bool { - return expectedGetAllOrder[i].ChainId < expectedGetAllOrder[j].ChainId - }) - - _, found := pk.GetInitTimeoutTimestamp(ctx, timeoutTimestamps[0].ChainId) - require.False(t, found) - - for _, tt := range timeoutTimestamps { - pk.SetInitTimeoutTimestamp(ctx, tt.ChainId, tt.Timestamp) - } - - for _, tt := range timeoutTimestamps { - _, found := pk.GetInitTimeoutTimestamp(ctx, tt.ChainId) - require.True(t, found) - } - - // iterate and check all results are returned in the expected order - result := pk.GetAllInitTimeoutTimestamps(ctx) - require.Len(t, result, len(timeoutTimestamps)) - require.Equal(t, result, expectedGetAllOrder) - - pk.DeleteInitTimeoutTimestamp(ctx, timeoutTimestamps[0].ChainId) - _, found = pk.GetInitTimeoutTimestamp(ctx, timeoutTimestamps[0].ChainId) - require.False(t, found) -} - -// TestVscSendTimestamp tests the set, deletion, and iteration methods for VSC timeout timestamps -func TestVscSendTimestamp(t *testing.T) { - providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - now := time.Now().UTC() - - testCases := []struct { - chainID string - ts time.Time - vscID uint64 - }{ - {chainID: "chain", ts: now.Add(2 * time.Hour), vscID: 2}, - {chainID: "chain", ts: now.Add(time.Hour), vscID: 1}, - {chainID: "chain", ts: now.Add(time.Hour), vscID: 3}, - // this is not possible since the ts is the timestamp of sending, - // which means it must be in the same order as vscIDs, - // but it still worth testing - {chainID: "chain", ts: now.Add(-time.Hour), vscID: 4}, - {chainID: "chain1", ts: now.Add(time.Hour), vscID: 1}, - {chainID: "chain2", ts: now.Add(time.Hour), vscID: 1}, - } - chainID := testCases[0].chainID - expectedGetAllOrder := []types.VscSendTimestamp{} - for _, tc := range testCases { - if tc.chainID == chainID { - expectedGetAllOrder = append(expectedGetAllOrder, types.VscSendTimestamp{VscId: tc.vscID, Timestamp: tc.ts}) - } - } - // sorting by vscID - sort.Slice(expectedGetAllOrder, func(i, j int) bool { - return expectedGetAllOrder[i].VscId < expectedGetAllOrder[j].VscId - }) - - require.Empty(t, providerKeeper.GetAllVscSendTimestamps(ctx, chainID)) - - for _, tc := range testCases { - providerKeeper.SetVscSendTimestamp(ctx, tc.chainID, tc.vscID, tc.ts) - } - - // iterate and check all results are returned in the expected order - vscSendTimestamps := providerKeeper.GetAllVscSendTimestamps(ctx, chainID) - require.Equal(t, expectedGetAllOrder, vscSendTimestamps) - - vscSendTimestamp, found := providerKeeper.GetFirstVscSendTimestamp(ctx, chainID) - require.True(t, found) - require.Equal(t, vscSendTimestamp, expectedGetAllOrder[0]) - - // delete first VSC send timestamp - providerKeeper.DeleteVscSendTimestamp(ctx, chainID, vscSendTimestamp.VscId) - for _, vst := range providerKeeper.GetAllVscSendTimestamps(ctx, chainID) { - require.NotEqual(t, vscSendTimestamp, vst) - } - - // delete all VSC send timestamps - providerKeeper.DeleteVscSendTimestampsForConsumer(ctx, chainID) - require.Empty(t, providerKeeper.GetAllVscSendTimestamps(ctx, chainID)) -} - func TestGetAllRegisteredConsumerChainIDs(t *testing.T) { pk, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() @@ -433,92 +264,6 @@ func TestGetAllChannelToChains(t *testing.T) { require.Equal(t, expectedGetAllOrder, result) } -// TestGetAllUnbondingOps tests GetAllUnbondingOps behaviour correctness -func TestGetAllUnbondingOps(t *testing.T) { - pk, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - ops := []types.UnbondingOp{ - { - Id: 2, - UnbondingConsumerChains: []string{"chain-2", "chain-1"}, - }, - { - Id: 1, - UnbondingConsumerChains: []string{"chain-1", "chain-2"}, - }, - { - Id: 4, - UnbondingConsumerChains: []string{"chain-2"}, - }, - { - Id: 3, - UnbondingConsumerChains: []string{"chain-3", "chain-1", "chain-2"}, - }, - } - expectedGetAllOrder := ops - // sorting by Id - sort.Slice(expectedGetAllOrder, func(i, j int) bool { - return expectedGetAllOrder[i].Id < expectedGetAllOrder[j].Id - }) - - for _, op := range ops { - pk.SetUnbondingOp(ctx, op) - } - - // iterate and check all results are returned - result := pk.GetAllUnbondingOps(ctx) - require.Len(t, result, len(ops)) - require.Equal(t, expectedGetAllOrder, result) -} - -// TestRemoveConsumerFromUnbondingOp tests RemoveConsumerFromUnbondingOp behaviour correctness -func TestRemoveConsumerFromUnbondingOp(t *testing.T) { - pk, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - var expectedID uint64 = 1 - expectedUnbondingOp := types.UnbondingOp{ - Id: expectedID, - UnbondingConsumerChains: []string{"chain-3", "chain-1", "chain-2"}, - } - - pk.SetUnbondingOp(ctx, expectedUnbondingOp) - - canComplete := pk.RemoveConsumerFromUnbondingOp(ctx, expectedID, "chain-1") - require.False(t, canComplete) - unbondingOp, found := pk.GetUnbondingOp(ctx, expectedID) - require.True(t, found) - expectedChainIDs := []string{"chain-3", "chain-2"} - require.Equal(t, expectedChainIDs, unbondingOp.UnbondingConsumerChains) - - canComplete = pk.RemoveConsumerFromUnbondingOp(ctx, expectedID, "chain-2") - require.False(t, canComplete) - unbondingOp, found = pk.GetUnbondingOp(ctx, expectedID) - require.True(t, found) - expectedChainIDs = []string{"chain-3"} - require.Equal(t, expectedChainIDs, unbondingOp.UnbondingConsumerChains) - - // check that it doesn't panic when calling with same chain ID - canComplete = pk.RemoveConsumerFromUnbondingOp(ctx, expectedID, "chain-2") - require.False(t, canComplete) - unbondingOp, found = pk.GetUnbondingOp(ctx, expectedID) - require.True(t, found) - require.Equal(t, expectedChainIDs, unbondingOp.UnbondingConsumerChains) - - canComplete = pk.RemoveConsumerFromUnbondingOp(ctx, expectedID, "chain-3") - require.True(t, canComplete) - unbondingOp, found = pk.GetUnbondingOp(ctx, expectedID) - require.False(t, found) - require.Empty(t, unbondingOp.UnbondingConsumerChains) - - // check that it panics when calling with wrong chain IDs - require.Panics(t, func() { - canComplete = pk.RemoveConsumerFromUnbondingOp(ctx, expectedID, "some_chain") - require.False(t, canComplete) - }) -} - // TestSetSlashLog tests slash log getter and setter methods func TestSetSlashLog(t *testing.T) { providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) @@ -744,38 +489,6 @@ func TestConsumerCommissionRate(t *testing.T) { 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)) @@ -832,32 +545,124 @@ func TestDenylist(t *testing.T) { require.True(t, providerKeeper.IsDenylistEmpty(ctx, chainID)) } -func TestMinimumPowerInTopN(t *testing.T) { +// TestAllowInactiveValidators tests the `SetAllowInactiveValidators` and `AllowsInactiveValidators` methods +func TestAllowInactiveValidators(t *testing.T) { k, ctx, _, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - chainID := "testChain" - minPower := int64(1000) + chainID := "chainID" - // Set the minimum power in top N - k.SetMinimumPowerInTopN(ctx, chainID, minPower) + // check that by default, AllowsInactiveValidators returns false + require.False(t, k.AllowsInactiveValidators(ctx, chainID)) - // Retrieve the minimum power in top N - actualMinPower, found := k.GetMinimumPowerInTopN(ctx, chainID) - require.True(t, found) - require.Equal(t, minPower, actualMinPower) + // set the chain to allow inactive validators + k.SetInactiveValidatorsAllowed(ctx, chainID, true) - // Update the minimum power - newMinPower := int64(2000) - k.SetMinimumPowerInTopN(ctx, chainID, newMinPower) + // check that AllowsInactiveValidators returns true + require.True(t, k.AllowsInactiveValidators(ctx, chainID)) - // Retrieve the updated minimum power in top N - newActualMinPower, found := k.GetMinimumPowerInTopN(ctx, chainID) - require.True(t, found) - require.Equal(t, newMinPower, newActualMinPower) + // set the chain to not allow inactive validators + k.SetInactiveValidatorsAllowed(ctx, chainID, false) - // 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) + // check that AllowsInactiveValidators returns false + require.False(t, k.AllowsInactiveValidators(ctx, chainID)) +} + +// Tests setting, getting and deleting parameters that are stored per-consumer chain. +// The tests cover the following parameters: +// - MinimumPowerInTopN +// - MinStake +// - ValidatorSetCap +// - ValidatorPowersCap +func TestKeeperConsumerParams(t *testing.T) { + k, ctx, _, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + + tests := []struct { + name string + settingFunc func(sdk.Context, string, int64) + getFunc func(sdk.Context, string) (int64, bool) + deleteFunc func(sdk.Context, string) + initialValue int64 + updatedValue int64 + }{ + { + name: "Minimum Power In Top N", + settingFunc: func(ctx sdk.Context, id string, val int64) { k.SetMinimumPowerInTopN(ctx, id, val) }, + getFunc: func(ctx sdk.Context, id string) (int64, bool) { return k.GetMinimumPowerInTopN(ctx, id) }, + deleteFunc: func(ctx sdk.Context, id string) { k.DeleteMinimumPowerInTopN(ctx, id) }, + initialValue: 1000, + updatedValue: 2000, + }, + { + name: "Minimum Stake", + settingFunc: func(ctx sdk.Context, id string, val int64) { k.SetMinStake(ctx, id, uint64(val)) }, + getFunc: func(ctx sdk.Context, id string) (int64, bool) { + val, found := k.GetMinStake(ctx, id) + return int64(val), found + }, + deleteFunc: func(ctx sdk.Context, id string) { k.DeleteMinStake(ctx, id) }, + initialValue: 1000, + updatedValue: 2000, + }, + { + name: "Validator Set Cap", + settingFunc: func(ctx sdk.Context, id string, val int64) { k.SetValidatorSetCap(ctx, id, uint32(val)) }, + getFunc: func(ctx sdk.Context, id string) (int64, bool) { + val, found := k.GetValidatorSetCap(ctx, id) + return int64(val), found + }, + deleteFunc: func(ctx sdk.Context, id string) { k.DeleteValidatorSetCap(ctx, id) }, + initialValue: 10, + updatedValue: 200, + }, + { + name: "Validator Powers Cap", + settingFunc: func(ctx sdk.Context, id string, val int64) { k.SetValidatorsPowerCap(ctx, id, uint32(val)) }, + getFunc: func(ctx sdk.Context, id string) (int64, bool) { + val, found := k.GetValidatorsPowerCap(ctx, id) + return int64(val), found + }, + deleteFunc: func(ctx sdk.Context, id string) { k.DeleteValidatorsPowerCap(ctx, id) }, + initialValue: 10, + updatedValue: 11, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + chainID := "chainID" + // Set initial value + tt.settingFunc(ctx, chainID, int64(tt.initialValue)) + + // Retrieve and check initial value + actualValue, found := tt.getFunc(ctx, chainID) + require.True(t, found) + require.EqualValues(t, tt.initialValue, actualValue) + + // Update value + tt.settingFunc(ctx, chainID, int64(tt.updatedValue)) + + // Retrieve and check updated value + newActualValue, found := tt.getFunc(ctx, chainID) + require.True(t, found) + require.EqualValues(t, tt.updatedValue, newActualValue) + + // Check non-existent chain ID + _, found = tt.getFunc(ctx, "not the chainID") + require.False(t, found) + + // Delete value + tt.deleteFunc(ctx, chainID) + + // Check that value was deleted + _, found = tt.getFunc(ctx, chainID) + require.False(t, found) + + // Try deleting again + tt.deleteFunc(ctx, chainID) + + // Check that the value is still deleted + _, found = tt.getFunc(ctx, chainID) + require.False(t, found) + }) + } } diff --git a/x/ccv/provider/keeper/key_assignment.go b/x/ccv/provider/keeper/key_assignment.go index 93bad8eb97..23dd20109d 100644 --- a/x/ccv/provider/keeper/key_assignment.go +++ b/x/ccv/provider/keeper/key_assignment.go @@ -3,6 +3,7 @@ package keeper import ( "encoding/base64" "fmt" + "time" errorsmod "cosmossdk.io/errors" storetypes "cosmossdk.io/store/types" @@ -104,25 +105,26 @@ func (k Keeper) SetValidatorConsumerPubKey( // If chainID is nil, it returns all the validators public keys assigned for all consumer chains // // Note that the validators public keys assigned for a consumer chain are stored under keys -// with the following format: UnbondingOpIndexBytePrefix | len(chainID) | chainID | providerAddress +// with the following format: ConsumerValidatorsBytePrefix | len(chainID) | chainID | providerAddress // Thus, the returned array is // - in ascending order of providerAddresses, if chainID is not nil; // - in undetermined order, if chainID is nil. func (k Keeper) GetAllValidatorConsumerPubKeys(ctx sdk.Context, chainID *string) (validatorConsumerPubKeys []types.ValidatorConsumerPubKey) { store := ctx.KVStore(k.storeKey) var prefix []byte + consumerValidatorsKeyPrefix := types.ConsumerValidatorsKeyPrefix() if chainID == nil { // iterate over the validators public keys assigned for all consumer chains - prefix = []byte{types.ConsumerValidatorsBytePrefix} + prefix = []byte{consumerValidatorsKeyPrefix} } else { // iterate over the validators public keys assigned for chainID - prefix = types.ChainIdWithLenKey(types.ConsumerValidatorsBytePrefix, *chainID) + prefix = types.ChainIdWithLenKey(consumerValidatorsKeyPrefix, *chainID) } iterator := storetypes.KVStorePrefixIterator(store, prefix) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { // TODO: store chainID and provider cons address in value bytes, marshaled as protobuf type - chainID, providerAddrTmp, err := types.ParseChainIdAndConsAddrKey(types.ConsumerValidatorsBytePrefix, iterator.Key()) + chainID, providerAddrTmp, err := types.ParseChainIdAndConsAddrKey(consumerValidatorsKeyPrefix, iterator.Key()) if err != nil { // An error here would indicate something is very wrong, // the store key is assumed to be correctly serialized in SetValidatorConsumerPubKey. @@ -188,25 +190,26 @@ func (k Keeper) SetValidatorByConsumerAddr( // If chainID is nil, it returns all the mappings from consensus addresses on all consumer chains. // // Note that the mappings for a consumer chain are stored under keys with the following format: -// ValidatorsByConsumerAddrBytePrefix | len(chainID) | chainID | consumerAddress +// ValidatorsByConsumerAddrKeyPrefix | len(chainID) | chainID | consumerAddress // Thus, the returned array is // - in ascending order of consumerAddresses, if chainID is not nil; // - in undetermined order, if chainID is nil. func (k Keeper) GetAllValidatorsByConsumerAddr(ctx sdk.Context, chainID *string) (validatorConsumerAddrs []types.ValidatorByConsumerAddr) { store := ctx.KVStore(k.storeKey) var prefix []byte + validatorsByConsumerAddrKeyPrefix := types.ValidatorsByConsumerAddrKeyPrefix() if chainID == nil { // iterate over the mappings from consensus addresses on all consumer chains - prefix = []byte{types.ValidatorsByConsumerAddrBytePrefix} + prefix = []byte{validatorsByConsumerAddrKeyPrefix} } else { // iterate over the mappings from consensus addresses on chainID - prefix = types.ChainIdWithLenKey(types.ValidatorsByConsumerAddrBytePrefix, *chainID) + prefix = types.ChainIdWithLenKey(validatorsByConsumerAddrKeyPrefix, *chainID) } iterator := storetypes.KVStorePrefixIterator(store, prefix) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { // TODO: store chainID and consumer cons address in value bytes, marshaled as protobuf type - chainID, consumerAddrTmp, err := types.ParseChainIdAndConsAddrKey(types.ValidatorsByConsumerAddrBytePrefix, iterator.Key()) + chainID, consumerAddrTmp, err := types.ParseChainIdAndConsAddrKey(validatorsByConsumerAddrKeyPrefix, iterator.Key()) if err != nil { // An error here would indicate something is very wrong, // store keys are assumed to be correctly serialized in SetValidatorByConsumerAddr. @@ -233,16 +236,22 @@ func (k Keeper) DeleteValidatorByConsumerAddr(ctx sdk.Context, chainID string, c } // AppendConsumerAddrsToPrune appends a consumer validator address to the list of consumer addresses -// that can be pruned once the VSCMaturedPacket with vscID is received. +// that can be pruned once the block time is at least pruneTs. // // The following invariant needs to hold: // For each consumer address cAddr in ValidatorByConsumerAddr, // - either there exists a provider address pAddr in ValidatorConsumerPubKey, // s.t. hash(ValidatorConsumerPubKey(pAddr)) = cAddr -// - or there exists a vscID in ConsumerAddrsToPrune s.t. cAddr in ConsumerAddrsToPrune(vscID) -func (k Keeper) AppendConsumerAddrsToPrune(ctx sdk.Context, chainID string, vscID uint64, consumerAddr types.ConsumerConsAddress) { +// - or there exists a timestamp in ConsumerAddrsToPrune s.t. cAddr in ConsumerAddrsToPrune(timestamp) +func (k Keeper) AppendConsumerAddrsToPrune( + ctx sdk.Context, + chainID string, + pruneTs time.Time, + consumerAddr types.ConsumerConsAddress, +) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.ConsumerAddrsToPruneKey(chainID, vscID)) + storeKey := types.ConsumerAddrsToPruneV2Key(chainID, pruneTs) + bz := store.Get(storeKey) var consumerAddrsToPrune types.AddressList if bz != nil { err := consumerAddrsToPrune.Unmarshal(bz) @@ -259,18 +268,19 @@ func (k Keeper) AppendConsumerAddrsToPrune(ctx sdk.Context, chainID string, vscI // consumerAddrsToPrune is instantiated in this method and should be able to be marshaled. panic(err) } - store.Set(types.ConsumerAddrsToPruneKey(chainID, vscID), bz) + store.Set(storeKey, bz) } -// GetConsumerAddrsToPrune returns the list of consumer addresses -// that can be pruned once the VSCMaturedPacket with vscID is received +// GetConsumerAddrsToPrune returns the list of consumer addresses to prune stored under timestamp ts. +// Note that this method is only used in testing. func (k Keeper) GetConsumerAddrsToPrune( ctx sdk.Context, chainID string, - vscID uint64, + ts time.Time, ) (consumerAddrsToPrune types.AddressList) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.ConsumerAddrsToPruneKey(chainID, vscID)) + + bz := store.Get(types.ConsumerAddrsToPruneV2Key(chainID, ts)) if bz == nil { return } @@ -283,18 +293,78 @@ func (k Keeper) GetConsumerAddrsToPrune( return } -// GetAllConsumerAddrsToPrune gets all consumer addresses that can be pruned for a given chainID. +// ConsumeConsumerAddrsToPrune returns the list of consumer addresses that can be pruned at timestamp ts. +// The returned addresses are removed from the store. // // Note that the list of all consumer addresses is stored under keys with the following format: -// ConsumerAddrsToPruneBytePrefix | len(chainID) | chainID | vscID -// Thus, the returned array is in ascending order of vscIDs. -func (k Keeper) GetAllConsumerAddrsToPrune(ctx sdk.Context, chainID string) (consumerAddrsToPrune []types.ConsumerAddrsToPrune) { +// ConsumerAddrsToPruneV2BytePrefix | len(chainID) | chainID | timestamp +// Thus, this method returns all the consumer addresses stored under keys in the following range: +// (ConsumerAddrsToPruneV2BytePrefix | len(chainID) | chainID | ts') where ts' <= ts +func (k Keeper) ConsumeConsumerAddrsToPrune( + ctx sdk.Context, + chainID string, + ts time.Time, +) (consumerAddrsToPrune types.AddressList) { store := ctx.KVStore(k.storeKey) - iteratorPrefix := types.ChainIdWithLenKey(types.ConsumerAddrsToPruneBytePrefix, chainID) + consumerAddrsToPruneKeyPrefix := types.ConsumerAddrsToPruneV2KeyPrefix() + startPrefix := types.ChainIdWithLenKey(consumerAddrsToPruneKeyPrefix, chainID) + iterator := store.Iterator(startPrefix, + storetypes.InclusiveEndBytes(types.ConsumerAddrsToPruneV2Key(chainID, ts))) + defer iterator.Close() + + var keysToDel [][]byte + for ; iterator.Valid(); iterator.Next() { + // Sanity check + if _, pruneTs, err := types.ParseChainIdAndTsKey(consumerAddrsToPruneKeyPrefix, iterator.Key()); err != nil { + // An error here would indicate something is very wrong, + // store keys are assumed to be correctly serialized in AppendConsumerAddrsToPrune. + k.Logger(ctx).Error("ParseChainIdAndTsKey failed", + "key", string(iterator.Key()), + "error", err.Error(), + ) + continue + } else if pruneTs.After(ts) { + // An error here would indicate something is wrong the iterator + k.Logger(ctx).Error("iterator in ConsumeConsumerAddrsToPrune failed", "key", string(iterator.Key())) + continue + } + + keysToDel = append(keysToDel, iterator.Key()) + + var addrs types.AddressList + if err := addrs.Unmarshal(iterator.Value()); err != nil { + // An error here would indicate something is very wrong, + // the list of consumer addresses is assumed to be correctly serialized in AppendConsumerAddrsToPrune. + k.Logger(ctx).Error("unmarshaling in ConsumeConsumerAddrsToPrune failed", + "key", string(iterator.Key()), + "error", err.Error(), + ) + continue + } + + consumerAddrsToPrune.Addresses = append(consumerAddrsToPrune.Addresses, addrs.Addresses...) + } + + for _, delKey := range keysToDel { + store.Delete(delKey) + } + + return +} + +// GetAllConsumerAddrsToPrune gets all consumer addresses that can be eventually pruned for a given chainID. +// +// Note that the list of all consumer addresses is stored under keys with the following format: +// ConsumerAddrsToPruneV2BytePrefix | len(chainID) | chainID | timestamp +// Thus, the returned array is in ascending order of timestamps. +func (k Keeper) GetAllConsumerAddrsToPrune(ctx sdk.Context, chainID string) (consumerAddrsToPrune []types.ConsumerAddrsToPruneV2) { + store := ctx.KVStore(k.storeKey) + consumerAddrsToPruneKeyPrefix := types.ConsumerAddrsToPruneV2KeyPrefix() + iteratorPrefix := types.ChainIdWithLenKey(consumerAddrsToPruneKeyPrefix, chainID) iterator := storetypes.KVStorePrefixIterator(store, iteratorPrefix) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - _, vscID, err := types.ParseChainIdAndUintIdKey(types.ConsumerAddrsToPruneBytePrefix, iterator.Key()) + _, ts, err := types.ParseChainIdAndTsKey(consumerAddrsToPruneKeyPrefix, iterator.Key()) if err != nil { // An error here would indicate something is very wrong, // store keys are assumed to be correctly serialized in AppendConsumerAddrsToPrune. @@ -308,8 +378,8 @@ func (k Keeper) GetAllConsumerAddrsToPrune(ctx sdk.Context, chainID string) (con panic(err) } - consumerAddrsToPrune = append(consumerAddrsToPrune, types.ConsumerAddrsToPrune{ - VscId: vscID, + consumerAddrsToPrune = append(consumerAddrsToPrune, types.ConsumerAddrsToPruneV2{ + PruneTs: ts, ConsumerAddrs: &addrs, ChainId: chainID, }) @@ -318,10 +388,10 @@ func (k Keeper) GetAllConsumerAddrsToPrune(ctx sdk.Context, chainID string) (con return consumerAddrsToPrune } -// DeleteConsumerAddrsToPrune deletes the list of consumer addresses mapped to a given VSC ID -func (k Keeper) DeleteConsumerAddrsToPrune(ctx sdk.Context, chainID string, vscID uint64) { +// DeleteConsumerAddrsToPruneV2 deletes the list of consumer addresses mapped to a timestamp +func (k Keeper) DeleteConsumerAddrsToPrune(ctx sdk.Context, chainID string, pruneTs time.Time) { store := ctx.KVStore(k.storeKey) - store.Delete(types.ConsumerAddrsToPruneKey(chainID, vscID)) + store.Delete(types.ConsumerAddrsToPruneV2Key(chainID, pruneTs)) } // AssignConsumerKey assigns the consumerKey to the validator with providerAddr @@ -392,13 +462,16 @@ func (k Keeper) AssignConsumerKey( // check whether the consumer chain is already registered, // i.e., a client to the consumer was already created if _, consumerRegistered := k.GetConsumerClientId(ctx, chainID); consumerRegistered { - // mark the old consumer address as prunable once the VSCMaturedPacket - // for the current VSC ID is received; - // note: this state is removed on receiving the VSCMaturedPacket + // mark the old consumer address as prunable once UnbondingPeriod elapses; + // note: this state is removed on EndBlock + unbondingPeriod, err := k.stakingKeeper.UnbondingTime(ctx) + if err != nil { + return err + } k.AppendConsumerAddrsToPrune( ctx, chainID, - k.GetValidatorSetUpdateId(ctx), + ctx.BlockTime().Add(unbondingPeriod), oldConsumerAddr, ) } else { @@ -439,8 +512,10 @@ func (k Keeper) GetProviderAddrFromConsumerAddr( // PruneKeyAssignments prunes the consumer addresses no longer needed // as they cannot be referenced in slash requests (by a correct consumer) -func (k Keeper) PruneKeyAssignments(ctx sdk.Context, chainID string, vscID uint64) { - consumerAddrs := k.GetConsumerAddrsToPrune(ctx, chainID, vscID) +func (k Keeper) PruneKeyAssignments(ctx sdk.Context, chainID string) { + now := ctx.BlockTime() + + consumerAddrs := k.ConsumeConsumerAddrsToPrune(ctx, chainID, now) for _, addrBz := range consumerAddrs.Addresses { consumerAddr := types.NewConsumerConsAddress(addrBz) k.DeleteValidatorByConsumerAddr(ctx, chainID, consumerAddr) @@ -449,8 +524,6 @@ func (k Keeper) PruneKeyAssignments(ctx sdk.Context, chainID string, vscID uint6 "consumer consensus addr", consumerAddr.String(), ) } - - k.DeleteConsumerAddrsToPrune(ctx, chainID, vscID) } // DeleteKeyAssignments deletes all the state needed for key assignments on a consumer chain @@ -469,7 +542,7 @@ func (k Keeper) DeleteKeyAssignments(ctx sdk.Context, chainID string) { // delete ValidatorConsumerPubKey for _, consumerAddrsToPrune := range k.GetAllConsumerAddrsToPrune(ctx, chainID) { - k.DeleteConsumerAddrsToPrune(ctx, chainID, consumerAddrsToPrune.VscId) + k.DeleteConsumerAddrsToPrune(ctx, chainID, consumerAddrsToPrune.PruneTs) } } diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index 2f67890b00..7dbec09f40 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -189,25 +189,52 @@ func TestGetAllValidatorsByConsumerAddr(t *testing.T) { func TestConsumerAddrsToPruneCRUD(t *testing.T) { chainID := consumer - consumerAddr := types.NewConsumerConsAddress([]byte("consumerAddr1")) - vscID := uint64(1) + consumerAddr1 := types.NewConsumerConsAddress([]byte("consumerAddr1")) + consumerAddr2 := types.NewConsumerConsAddress([]byte("consumerAddr2")) keeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() - addrsToPrune := keeper.GetConsumerAddrsToPrune(ctx, chainID, vscID).Addresses + ts1 := ctx.BlockTime() + ts2 := ts1.Add(time.Hour) + + addrsToPrune := keeper.GetConsumerAddrsToPrune(ctx, chainID, ts1).Addresses require.Empty(t, addrsToPrune) - keeper.AppendConsumerAddrsToPrune(ctx, chainID, vscID, consumerAddr) + keeper.AppendConsumerAddrsToPrune(ctx, chainID, ts1, consumerAddr1) + + addrsToPrune = keeper.GetConsumerAddrsToPrune(ctx, chainID, ts1).Addresses + require.NotEmpty(t, addrsToPrune, "addresses to prune is empty") + require.Len(t, addrsToPrune, 1, "addresses to prune is not len 1") + require.Equal(t, addrsToPrune[0], consumerAddr1.ToSdkConsAddr().Bytes()) + + keeper.AppendConsumerAddrsToPrune(ctx, chainID, ts2, consumerAddr2) - addrsToPrune = keeper.GetConsumerAddrsToPrune(ctx, chainID, vscID).Addresses + addrsToPrune = keeper.GetConsumerAddrsToPrune(ctx, chainID, ts2).Addresses require.NotEmpty(t, addrsToPrune, "addresses to prune is empty") require.Len(t, addrsToPrune, 1, "addresses to prune is not len 1") - require.Equal(t, addrsToPrune[0], consumerAddr.ToSdkConsAddr().Bytes()) + require.Equal(t, addrsToPrune[0], consumerAddr2.ToSdkConsAddr().Bytes()) + + keeper.DeleteConsumerAddrsToPrune(ctx, chainID, ts1) + addrsToPrune = keeper.GetConsumerAddrsToPrune(ctx, chainID, ts1).Addresses + require.Empty(t, addrsToPrune, "addresses to prune was returned") + addrsToPrune = keeper.GetConsumerAddrsToPrune(ctx, chainID, ts2).Addresses + require.NotEmpty(t, addrsToPrune, "addresses to prune is empty") + require.Len(t, addrsToPrune, 1, "addresses to prune is not len 1") + require.Equal(t, addrsToPrune[0], consumerAddr2.ToSdkConsAddr().Bytes()) + + keeper.AppendConsumerAddrsToPrune(ctx, chainID, ts1, consumerAddr1) - keeper.DeleteConsumerAddrsToPrune(ctx, chainID, vscID) - addrsToPrune = keeper.GetConsumerAddrsToPrune(ctx, chainID, vscID).Addresses + addrsToPrune = keeper.ConsumeConsumerAddrsToPrune(ctx, chainID, ts1).Addresses + require.NotEmpty(t, addrsToPrune, "addresses to prune was returned") + require.Len(t, addrsToPrune, 1, "addresses to prune is not len 1") + require.Equal(t, addrsToPrune[0], consumerAddr1.ToSdkConsAddr().Bytes()) + addrsToPrune = keeper.GetConsumerAddrsToPrune(ctx, chainID, ts1).Addresses require.Empty(t, addrsToPrune, "addresses to prune was returned") + addrsToPrune = keeper.GetConsumerAddrsToPrune(ctx, chainID, ts2).Addresses + require.NotEmpty(t, addrsToPrune, "addresses to prune is empty") + require.Len(t, addrsToPrune, 1, "addresses to prune is not len 1") + require.Equal(t, addrsToPrune[0], consumerAddr2.ToSdkConsAddr().Bytes()) } func TestGetAllConsumerAddrsToPrune(t *testing.T) { @@ -219,7 +246,7 @@ func TestGetAllConsumerAddrsToPrune(t *testing.T) { chainIDs := []string{"consumer-1", "consumer-2", "consumer-3"} numAssignments := 10 - testAssignments := []types.ConsumerAddrsToPrune{} + testAssignments := []types.ConsumerAddrsToPruneV2{} for i := 0; i < numAssignments; i++ { consumerAddresses := types.AddressList{} for j := 0; j < 2*(i+1); j++ { @@ -227,9 +254,9 @@ func TestGetAllConsumerAddrsToPrune(t *testing.T) { consumerAddresses.Addresses = append(consumerAddresses.Addresses, addr) } testAssignments = append(testAssignments, - types.ConsumerAddrsToPrune{ + types.ConsumerAddrsToPruneV2{ ChainId: chainIDs[rng.Intn(len(chainIDs))], - VscId: rng.Uint64(), + PruneTs: time.Now().UTC(), ConsumerAddrs: &consumerAddresses, }, ) @@ -248,21 +275,21 @@ func TestGetAllConsumerAddrsToPrune(t *testing.T) { break } } - expectedGetAllOrder := []types.ConsumerAddrsToPrune{} + expectedGetAllOrder := []types.ConsumerAddrsToPruneV2{} for _, assignment := range testAssignments { if assignment.ChainId == chainID { expectedGetAllOrder = append(expectedGetAllOrder, assignment) } } - // sorting by ConsumerAddrsToPrune.VscId + // sorting by ConsumerAddrsToPrune.PruneTs sort.Slice(expectedGetAllOrder, func(i, j int) bool { - return expectedGetAllOrder[i].VscId < expectedGetAllOrder[j].VscId + return expectedGetAllOrder[i].PruneTs.Before(expectedGetAllOrder[j].PruneTs) }) for _, assignment := range testAssignments { for _, addr := range assignment.ConsumerAddrs.Addresses { consumerAddr := types.NewConsumerConsAddress(addr) - pk.AppendConsumerAddrsToPrune(ctx, assignment.ChainId, assignment.VscId, consumerAddr) + pk.AppendConsumerAddrsToPrune(ctx, assignment.ChainId, assignment.PruneTs, consumerAddr) } } @@ -277,7 +304,7 @@ func checkCorrectPruningProperty(ctx sdk.Context, k providerkeeper.Keeper, chain For each consumer address cAddr in ValidatorByConsumerAddr, - either there exists a provider address pAddr in ValidatorConsumerPubKey, s.t. hash(ValidatorConsumerPubKey(pAddr)) = cAddr - - or there exists a vscID in ConsumerAddrsToPrune s.t. cAddr in ConsumerAddrsToPrune(vscID) + - or there exists a timestamp in ConsumerAddrsToPrune s.t. cAddr in ConsumerAddrsToPrune(timestamp) */ willBePruned := map[string]bool{} for _, consAddrToPrune := range k.GetAllConsumerAddrsToPrune(ctx, chainID) { @@ -293,6 +320,7 @@ func checkCorrectPruningProperty(ctx sdk.Context, k providerkeeper.Keeper, chain // Address will be pruned, everything is fine. continue } + // Try to find a validator who has this consumer address currently assigned isCurrentlyAssigned := false for _, valconsPubKey := range k.GetAllValidatorConsumerPubKeys(ctx, &valByConsAddr.ChainId) { @@ -388,6 +416,7 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, consumerIdentities[1].SDKValConsAddress(), ).Return(stakingtypes.Validator{}, stakingtypes.ErrNoValidatorFound), + mocks.MockStakingKeeper.EXPECT().UnbondingTime(ctx), ) }, doActions: func(sdkCtx sdk.Context, k providerkeeper.Keeper) { @@ -714,11 +743,6 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { providerValset := CreateValSet(providerIDS) // NOTE: consumer must have space for provider identities because default key assignments are to provider keys consumerValset := CreateValSet(assignableIDS) - // For each validator on the consumer, record the corresponding provider - // address as looked up on the provider using GetProviderAddrFromConsumerAddr - // at a given vscid. - // consumer consAddr -> vscid -> provider consAddr - historicSlashQueries := map[string]map[uint64]string{} // Sanity check that the validator set update is initialised to 0, for clarity. require.Equal(t, k.GetValidatorSetUpdateId(ctx), uint64(0)) @@ -773,7 +797,9 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { func(providerAddr types.ProviderConsAddress) bool { return true }) - updates = providerkeeper.DiffValidators(k.GetConsumerValSet(ctx, CHAINID), nextValidators) + valSet, error := k.GetConsumerValSet(ctx, CHAINID) + require.NoError(t, error) + updates = providerkeeper.DiffValidators(valSet, nextValidators) k.SetConsumerValSet(ctx, CHAINID, nextValidators) consumerValset.apply(updates) @@ -789,9 +815,14 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { } } + // Set the unbonding time to 60s so that a key is prunable after 60s + unbondingTimeInNs := 60 * time.Second // 60 seconds + mocks.MockStakingKeeper.EXPECT().UnbondingTime(gomock.Any()).Return(unbondingTimeInNs, nil).AnyTimes() + // The consumer chain has not yet been registered // Apply some randomly generated key assignments assignments := getAssignments() + applyAssignments(assignments) // And generate a random provider valset which, in the real system, will // be put into the consumer genesis. @@ -802,15 +833,15 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { // Register the consumer chain k.SetConsumerClientId(ctx, ChainID, "") - // Analogous to the last vscid received from the consumer in a maturity - // Used to check the correct pruning property - greatestPrunedVSCID := -1 + // Set the greatest block time up to which keys have been pruned. At the beginning, no pruning has taken + // place, so we set `greatestPrunedBlockTime` to 0, and set the current block time to 1. + greatestPrunedBlockTime := int64(0) + ctx = ctx.WithBlockTime(time.Unix(0, 1)) // Simulate a number of 'blocks' // Each block consists of a number of random key assignment tx's // and a random set of validator power updates for block := 0; block < NUM_BLOCKS_PER_EXECUTION; block++ { - stakingUpdates = getStakingUpdates() assignments = getAssignments() @@ -818,13 +849,14 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { applyAssignments(assignments) applyUpdatesAndIncrementVSCID(stakingUpdates) - // Randomly fast forward the greatest pruned VSCID. This simulates - // delivery of maturity packets from the consumer chain. - prunedVscid := greatestPrunedVSCID + - // +1 and -1 because id was incremented (-1), (+1) to make upper bound inclusive - rng.Intn(int(k.GetValidatorSetUpdateId(ctx))+1-1-greatestPrunedVSCID) - k.PruneKeyAssignments(ctx, ChainID, uint64(prunedVscid)) - greatestPrunedVSCID = prunedVscid + // prune all keys that can be pruned up to the current block time + greatestPrunedBlockTime = ctx.BlockTime().UnixNano() + k.PruneKeyAssignments(ctx, ChainID) + + // Increase the block time by a small random amount up to UnbondingTime / 10. We do not increase the block time + // by UnbondingTime so that in the upcoming iteration of this `for` loop (i.e., new block), not all the keys + // previously (in this current block) set to be prunable are pruned. + ctx = ctx.WithBlockTime(time.Unix(0, ctx.BlockTime().UnixNano()+rng.Int63n(unbondingTimeInNs.Nanoseconds())/10)) /* @@ -880,20 +912,25 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { Property: Pruning (bounded storage) Check that all keys have been or will eventually be pruned. */ - require.True(t, checkCorrectPruningProperty(ctx, k, ChainID)) /* Property: Correct Consumer Initiated Slash Lookup - Check that since the last pruning, it has never been possible to query - two different provider addresses from the same consumer address. + Check that since the last pruning took place, it has never been possible to have + two different provider addresses for the same consumer address. We know that the queried provider address was correct at least once, from checking the validator set replication property. These two facts together guarantee that the slash lookup is always correct. */ - // Build up the historicSlashQueries data structure + // For each validator on the consumer, record the corresponding provider + // address as looked up on the provider using `GetProviderAddrFromConsumerAddr` + // at a given block time. + // consumer consAddr -> block time -> provider consAddr + consumerAddrToBlockTimeToProviderAddr := map[string]map[uint64]string{} + + // Build up the consumerAddrToBlockTimeToProviderAddr data structure for i := range consumerValset.identities { // For each active validator on the consumer chain consC := consumerValset.identities[i].ConsumerConsAddress() @@ -901,28 +938,25 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { // Get the provider who assigned the key consP := k.GetProviderAddrFromConsumerAddr(ctx, ChainID, consC) - if _, found := historicSlashQueries[consC.String()]; !found { - historicSlashQueries[consC.String()] = map[uint64]string{} + if _, found := consumerAddrToBlockTimeToProviderAddr[consC.String()]; !found { + consumerAddrToBlockTimeToProviderAddr[consC.String()] = map[uint64]string{} } - vscid := k.GetValidatorSetUpdateId(ctx) - 1 // -1 since it was incremented before - // Record the slash query result obtained at this block - historicSlashQueries[consC.String()][vscid] = consP.String() + consumerAddrToBlockTimeToProviderAddr[consC.String()][uint64(ctx.BlockTime().UnixNano())] = consP.String() } } - // Check that, for each address known the consumer at some block - // with vscid st. greatestPrunedVSCID < vscid, there were never - // conflicting slash query results. - for _, vscidToConsP := range historicSlashQueries { + // Check that, for each consumer address known at some block with blockTime st. greatestPrunedBlockTime < blockTime, + // there were never two providers with this consumer address. + for _, blockTimeToProviderAddr := range consumerAddrToBlockTimeToProviderAddr { seen := map[string]bool{} - for vscid, consP := range vscidToConsP { - if uint64(greatestPrunedVSCID) < vscid { - // The provider would have returned + for blockTime, consP := range blockTimeToProviderAddr { + if uint64(greatestPrunedBlockTime) < blockTime { seen[consP] = true } } - // No conflicts. + // Having len(seen) >= 2 implies that we had at least 2 different provider addresses that at some point + // had the exact same consumer address since the last pruning took place. This should not be possible! require.True(t, len(seen) < 2) } diff --git a/x/ccv/provider/keeper/legacy_proposal.go b/x/ccv/provider/keeper/legacy_proposal.go index 19ad553038..d14773e5ed 100644 --- a/x/ccv/provider/keeper/legacy_proposal.go +++ b/x/ccv/provider/keeper/legacy_proposal.go @@ -92,6 +92,8 @@ func (k Keeper) HandleLegacyConsumerModificationProposal(ctx sdk.Context, p *typ k.SetTopN(ctx, p.ChainId, p.Top_N) k.SetValidatorsPowerCap(ctx, p.ChainId, p.ValidatorsPowerCap) k.SetValidatorSetCap(ctx, p.ChainId, p.ValidatorSetCap) + k.SetMinStake(ctx, p.ChainId, p.MinStake) + k.SetInactiveValidatorsAllowed(ctx, p.ChainId, p.AllowInactiveVals) k.DeleteAllowlist(ctx, p.ChainId) for _, address := range p.Allowlist { @@ -123,11 +125,11 @@ func (k Keeper) HandleLegacyConsumerModificationProposal(ctx sdk.Context, p *typ 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, err := k.GetLastBondedValidators(ctx) + activeValidators, err := k.GetLastProviderConsensusActiveValidators(ctx) if err != nil { return err } - minPower, err := k.ComputeMinPowerInTopN(ctx, bondedValidators, p.Top_N) + minPower, err := k.ComputeMinPowerInTopN(ctx, activeValidators, p.Top_N) if err != nil { return err } diff --git a/x/ccv/provider/keeper/legacy_proposal_test.go b/x/ccv/provider/keeper/legacy_proposal_test.go index 06533a2503..23c2c6a948 100644 --- a/x/ccv/provider/keeper/legacy_proposal_test.go +++ b/x/ccv/provider/keeper/legacy_proposal_test.go @@ -62,6 +62,8 @@ func TestHandleLegacyConsumerAdditionProposal(t *testing.T) { 0, nil, nil, + 0, + false, ).(*providertypes.ConsumerAdditionProposal), blockTime: now, expAppendProp: true, @@ -92,6 +94,8 @@ func TestHandleLegacyConsumerAdditionProposal(t *testing.T) { 0, nil, nil, + 0, + false, ).(*providertypes.ConsumerAdditionProposal), blockTime: now, expAppendProp: false, @@ -107,7 +111,7 @@ func TestHandleLegacyConsumerAdditionProposal(t *testing.T) { if tc.expAppendProp { // Mock calls are only asserted if we expect a client to be created. - testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 1, []stakingtypes.Validator{}, []int64{}, 1) + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 1, []stakingtypes.Validator{}, 1) gomock.InOrder( testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, tc.prop.ChainId, clienttypes.NewHeight(2, 3))..., ) @@ -282,18 +286,24 @@ func TestHandleConsumerModificationProposal(t *testing.T) { providerKeeper.SetAllowlist(ctx, chainID, providertypes.NewProviderConsAddress([]byte("allowlistedAddr1"))) providerKeeper.SetAllowlist(ctx, chainID, providertypes.NewProviderConsAddress([]byte("allowlistedAddr2"))) providerKeeper.SetDenylist(ctx, chainID, providertypes.NewProviderConsAddress([]byte("denylistedAddr1"))) + providerKeeper.SetMinStake(ctx, chainID, 1000) + providerKeeper.SetInactiveValidatorsAllowed(ctx, chainID, true) expectedTopN := uint32(75) expectedValidatorsPowerCap := uint32(67) expectedValidatorSetCap := uint32(20) expectedAllowlistedValidator := "cosmosvalcons1wpex7anfv3jhystyv3eq20r35a" expectedDenylistedValidator := "cosmosvalcons1nx7n5uh0ztxsynn4sje6eyq2ud6rc6klc96w39" + expectedMinStake := uint64(0) + expectedAllowInactiveValidators := false proposal := providertypes.NewConsumerModificationProposal("title", "description", chainID, expectedTopN, expectedValidatorsPowerCap, expectedValidatorSetCap, []string{expectedAllowlistedValidator}, []string{expectedDenylistedValidator}, + expectedMinStake, + expectedAllowInactiveValidators, ).(*providertypes.ConsumerModificationProposal) err := providerKeeper.HandleLegacyConsumerModificationProposal(ctx, proposal) @@ -315,4 +325,10 @@ func TestHandleConsumerModificationProposal(t *testing.T) { require.NoError(t, err) require.Equal(t, 1, len(providerKeeper.GetDenyList(ctx, chainID))) require.Equal(t, providertypes.NewProviderConsAddress(denylistedValidator), providerKeeper.GetDenyList(ctx, chainID)[0]) + + actualMinStake, _ := providerKeeper.GetMinStake(ctx, chainID) + require.Equal(t, expectedMinStake, actualMinStake) + + actualAllowInactiveValidators := providerKeeper.AllowsInactiveValidators(ctx, chainID) + require.Equal(t, expectedAllowInactiveValidators, actualAllowInactiveValidators) } diff --git a/x/ccv/provider/keeper/msg_server.go b/x/ccv/provider/keeper/msg_server.go index 50320e8196..da6f63cf8a 100644 --- a/x/ccv/provider/keeper/msg_server.go +++ b/x/ccv/provider/keeper/msg_server.go @@ -104,7 +104,8 @@ func (k msgServer) ConsumerAddition(goCtx context.Context, msg *types.MsgConsume // ConsumerRemoval defines an RPC handler method for MsgConsumerRemoval func (k msgServer) ConsumerRemoval( goCtx context.Context, - msg *types.MsgConsumerRemoval) (*types.MsgConsumerRemovalResponse, error) { + msg *types.MsgConsumerRemoval, +) (*types.MsgConsumerRemovalResponse, error) { if k.GetAuthority() != msg.Authority { return nil, errorsmod.Wrapf(types.ErrUnauthorized, "expected %s, got %s", k.GetAuthority(), msg.Authority) } diff --git a/x/ccv/provider/keeper/params.go b/x/ccv/provider/keeper/params.go index da3dda92b7..9feb274189 100644 --- a/x/ccv/provider/keeper/params.go +++ b/x/ccv/provider/keeper/params.go @@ -30,25 +30,6 @@ func (k Keeper) GetCCVTimeoutPeriod(ctx sdk.Context) time.Duration { return params.CcvTimeoutPeriod } -// GetInitTimeoutPeriod returns the init timeout period -func (k Keeper) GetInitTimeoutPeriod(ctx sdk.Context) time.Duration { - params := k.GetParams(ctx) - return params.InitTimeoutPeriod -} - -// GetVscTimeoutPeriod returns the vsc timeout period -func (k Keeper) GetVscTimeoutPeriod(ctx sdk.Context) time.Duration { - params := k.GetParams(ctx) - return params.VscTimeoutPeriod -} - -// SetVscTimeoutPeriod sets the vsc timeout period -func (k Keeper) SetVscTimeoutPeriod(ctx sdk.Context, period time.Duration) { - params := k.GetParams(ctx) - params.VscTimeoutPeriod = period - k.SetParams(ctx, params) -} - // GetSlashMeterReplenishPeriod returns the period in which: // Once the slash meter becomes not-full, the slash meter is replenished after this period. func (k Keeper) GetSlashMeterReplenishPeriod(ctx sdk.Context) time.Duration { @@ -85,6 +66,13 @@ func (k Keeper) GetNumberOfEpochsToStartReceivingRewards(ctx sdk.Context) int64 return params.NumberOfEpochsToStartReceivingRewards } +// GetMaxProviderConsensusValidators returns the number of validators that will be passed on from the staking module +// to the consensus engine on the provider +func (k Keeper) GetMaxProviderConsensusValidators(ctx sdk.Context) int64 { + params := k.GetParams(ctx) + return params.MaxProviderConsensusValidators +} + // GetParams returns the paramset for the provider module func (k Keeper) GetParams(ctx sdk.Context) types.Params { store := ctx.KVStore(k.storeKey) diff --git a/x/ccv/provider/keeper/params_test.go b/x/ccv/provider/keeper/params_test.go index cb1dc3cfe0..947122b221 100644 --- a/x/ccv/provider/keeper/params_test.go +++ b/x/ccv/provider/keeper/params_test.go @@ -41,8 +41,6 @@ func TestParams(t *testing.T) { ), "0.25", 7*24*time.Hour, - 5*time.Hour, - 10*time.Minute, time.Hour, "0.4", sdk.Coin{ @@ -51,6 +49,7 @@ func TestParams(t *testing.T) { }, 600, 24, + 10, ) providerKeeper.SetParams(ctx, newParams) params = providerKeeper.GetParams(ctx) diff --git a/x/ccv/provider/keeper/partial_set_security.go b/x/ccv/provider/keeper/partial_set_security.go index 069eb219dc..3112124724 100644 --- a/x/ccv/provider/keeper/partial_set_security.go +++ b/x/ccv/provider/keeper/partial_set_security.go @@ -91,6 +91,9 @@ func (k Keeper) HandleOptOut(ctx sdk.Context, chainID string, providerAddr types // 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 { + // log the validator + k.Logger(ctx).Debug("Checking whether to opt in validator because of top N", "validator", val.GetOperator()) + valAddr, err := sdk.ValAddressFromBech32(val.GetOperator()) if err != nil { k.Logger(ctx).Error("could not retrieve validator address: %s: %s", @@ -111,9 +114,11 @@ func (k Keeper) OptInTopNValidators(ctx sdk.Context, chainID string, bondedValid continue } + k.Logger(ctx).Debug("Opting in validator", "validator", val.GetOperator()) + // 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 + } } } @@ -165,7 +170,7 @@ func (k Keeper) ComputeMinPowerInTopN(ctx sdk.Context, bondedValidators []stakin // 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 { +func (k Keeper) CapValidatorSet(ctx sdk.Context, chainID string, validators []types.ConsensusValidator) []types.ConsensusValidator { if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { // is a no-op if the chain is a Top N chain return validators @@ -186,7 +191,7 @@ func (k Keeper) CapValidatorSet(ctx sdk.Context, chainID string, validators []ty // 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 { +func (k Keeper) CapValidatorsPower(ctx sdk.Context, chainID string, validators []types.ConsensusValidator) []types.ConsensusValidator { if p, found := k.GetValidatorsPowerCap(ctx, chainID); found && p > 0 { return NoMoreThanPercentOfTheSum(validators, p) } else { @@ -196,7 +201,7 @@ func (k Keeper) CapValidatorsPower(ctx sdk.Context, chainID string, validators [ } // sum is a helper function to sum all the validators' power -func sum(validators []types.ConsumerValidator) int64 { +func sum(validators []types.ConsensusValidator) int64 { s := int64(0) for _, v := range validators { s = s + v.Power @@ -206,7 +211,7 @@ func sum(validators []types.ConsumerValidator) int64 { // 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 { +func NoMoreThanPercentOfTheSum(validators []types.ConsensusValidator, percent uint32) []types.ConsensusValidator { // 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 @@ -262,7 +267,7 @@ func NoMoreThanPercentOfTheSum(validators []types.ConsumerValidator, percent uin } } - updatedValidators := make([]types.ConsumerValidator, len(validators)) + updatedValidators := make([]types.ConsensusValidator, len(validators)) powerPerValidator := int64(0) remainingValidators := int64(validatorsWithPowerLessThanMaxPower) @@ -308,11 +313,45 @@ func (k Keeper) CanValidateChain(ctx sdk.Context, chainID string, providerAddr t !k.IsDenylisted(ctx, chainID, providerAddr)) } +// FulfillsMinStake returns true if the validator `providerAddr` has enough stake to validate chain `chainID` +// by checking its staked tokens against the minimum stake required to validate the chain. +func (k Keeper) FulfillsMinStake(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress) bool { + minStake, found := k.GetMinStake(ctx, chainID) + if !found { + return true + } + + validator, err := k.stakingKeeper.GetValidatorByConsAddr(ctx, providerAddr.Address) + if err != nil { + k.Logger(ctx).Error("could not retrieve validator by consensus address", "consensus address", providerAddr, "error", err) + return false + } + + // validator has enough stake to validate the chain + return validator.GetBondedTokens().GTE(math.NewIntFromUint64(minStake)) +} + // 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 { +func (k Keeper) ComputeNextValidators(ctx sdk.Context, chainID string, bondedValidators []stakingtypes.Validator) []types.ConsensusValidator { + // sort the bonded validators by number of staked tokens in descending order + sort.Slice(bondedValidators, func(i, j int) bool { + return bondedValidators[i].GetBondedTokens().GT(bondedValidators[j].GetBondedTokens()) + }) + + // if inactive validators are not allowed, only consider the first `MaxProviderConsensusValidators` validators + // since those are the ones that participate in consensus + allowInactiveVals := k.AllowsInactiveValidators(ctx, chainID) + if !allowInactiveVals { + // only leave the first MaxProviderConsensusValidators bonded validators + maxProviderConsensusVals := k.GetMaxProviderConsensusValidators(ctx) + if len(bondedValidators) > int(maxProviderConsensusVals) { + bondedValidators = bondedValidators[:maxProviderConsensusVals] + } + } + nextValidators := k.FilterValidators(ctx, chainID, bondedValidators, func(providerAddr types.ProviderConsAddress) bool { - return k.CanValidateChain(ctx, chainID, providerAddr) + return k.CanValidateChain(ctx, chainID, providerAddr) && k.FulfillsMinStake(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 2dfbfd3b82..e180554239 100644 --- a/x/ccv/provider/keeper/partial_set_security_test.go +++ b/x/ccv/provider/keeper/partial_set_security_test.go @@ -86,6 +86,7 @@ func TestHandleOptInWithConsumerKey(t *testing.T) { // 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.True(t, found) require.Equal(t, providerAddr, actualProviderConsAddr) } @@ -133,7 +134,7 @@ func TestHandleOptOutFromTopNChain(t *testing.T) { valDConsAddr, _ := valD.GetConsAddr() mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valDConsAddr).Return(valD, nil).AnyTimes() - testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 4, []stakingtypes.Validator{valA, valB, valC, valD}, []int64{1, 2, 3, 4}, -1) // -1 to allow mocks AnyTimes + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 4, []stakingtypes.Validator{valA, valB, valC, valD}, -1) // -1 to allow mocks AnyTimes // initialize the minPowerInTopN correctly minPowerInTopN, err := providerKeeper.ComputeMinPowerInTopN(ctx, []stakingtypes.Validator{valA, valB, valC, valD}, 50) @@ -389,24 +390,24 @@ 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{}, + validatorA := types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddrA"), + Power: 1, + PublicKey: &crypto.PublicKey{}, } - validatorB := types.ConsumerValidator{ - ProviderConsAddr: []byte("providerConsAddrB"), - Power: 2, - ConsumerPublicKey: &crypto.PublicKey{}, + validatorB := types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddrB"), + Power: 2, + PublicKey: &crypto.PublicKey{}, } - validatorC := types.ConsumerValidator{ - ProviderConsAddr: []byte("providerConsAddrC"), - Power: 3, - ConsumerPublicKey: &crypto.PublicKey{}, + validatorC := types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddrC"), + Power: 3, + PublicKey: &crypto.PublicKey{}, } - validators := []types.ConsumerValidator{validatorA, validatorB, validatorC} + validators := []types.ConsensusValidator{validatorA, validatorB, validatorC} consumerValidators := providerKeeper.CapValidatorSet(ctx, "chainID", validators) require.Equal(t, validators, consumerValidators) @@ -421,55 +422,55 @@ func TestCapValidatorSet(t *testing.T) { providerKeeper.SetValidatorSetCap(ctx, "chainID", 1) consumerValidators = providerKeeper.CapValidatorSet(ctx, "chainID", validators) - require.Equal(t, []types.ConsumerValidator{validatorC}, consumerValidators) + require.Equal(t, []types.ConsensusValidator{validatorC}, consumerValidators) providerKeeper.SetValidatorSetCap(ctx, "chainID", 2) consumerValidators = providerKeeper.CapValidatorSet(ctx, "chainID", validators) - require.Equal(t, []types.ConsumerValidator{validatorC, validatorB}, consumerValidators) + require.Equal(t, []types.ConsensusValidator{validatorC, validatorB}, consumerValidators) providerKeeper.SetValidatorSetCap(ctx, "chainID", 3) consumerValidators = providerKeeper.CapValidatorSet(ctx, "chainID", validators) - require.Equal(t, []types.ConsumerValidator{validatorC, validatorB, validatorA}, consumerValidators) + require.Equal(t, []types.ConsensusValidator{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{}, + validatorA := types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddrA"), + Power: 1, + PublicKey: &crypto.PublicKey{}, } - validatorB := types.ConsumerValidator{ - ProviderConsAddr: []byte("providerConsAddrB"), - Power: 2, - ConsumerPublicKey: &crypto.PublicKey{}, + validatorB := types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddrB"), + Power: 2, + PublicKey: &crypto.PublicKey{}, } - validatorC := types.ConsumerValidator{ - ProviderConsAddr: []byte("providerConsAddrC"), - Power: 3, - ConsumerPublicKey: &crypto.PublicKey{}, + validatorC := types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddrC"), + Power: 3, + PublicKey: &crypto.PublicKey{}, } - validatorD := types.ConsumerValidator{ - ProviderConsAddr: []byte("providerConsAddrD"), - Power: 4, - ConsumerPublicKey: &crypto.PublicKey{}, + validatorD := types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddrD"), + Power: 4, + PublicKey: &crypto.PublicKey{}, } - validators := []types.ConsumerValidator{validatorA, validatorB, validatorC, validatorD} + validators := []types.ConsensusValidator{validatorA, validatorB, validatorC, validatorD} - expectedValidators := make([]types.ConsumerValidator, len(validators)) + expectedValidators := make([]types.ConsensusValidator, 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) { + sortValidators := func(validators []types.ConsensusValidator) { sort.Slice(validators, func(i, j int) bool { return bytes.Compare(validators[i].ProviderConsAddr, validators[j].ProviderConsAddr) < 0 }) @@ -523,13 +524,13 @@ func TestNoMoreThanPercentOfTheSum(t *testing.T) { require.True(t, noMoreThanPercent(keeper.NoMoreThanPercentOfTheSum(createConsumerValidators(powers), percent), percent)) } -func createConsumerValidators(powers []int64) []types.ConsumerValidator { - var validators []types.ConsumerValidator +func createConsumerValidators(powers []int64) []types.ConsensusValidator { + var validators []types.ConsensusValidator for _, p := range powers { - validators = append(validators, types.ConsumerValidator{ - ProviderConsAddr: []byte("providerConsAddr"), - Power: p, - ConsumerPublicKey: &crypto.PublicKey{}, + validators = append(validators, types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddr"), + Power: p, + PublicKey: &crypto.PublicKey{}, }) } return validators @@ -537,7 +538,7 @@ func createConsumerValidators(powers []int64) []types.ConsumerValidator { // 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 { +func noMoreThanPercent(validators []types.ConsensusValidator, percent uint32) bool { sum := int64(0) for _, v := range validators { sum = sum + v.Power @@ -551,7 +552,7 @@ func noMoreThanPercent(validators []types.ConsumerValidator, percent uint32) boo return true } -func sumPowers(vals []types.ConsumerValidator) int64 { +func sumPowers(vals []types.ConsensusValidator) int64 { sum := int64(0) for _, v := range vals { sum += v.Power @@ -559,7 +560,7 @@ func sumPowers(vals []types.ConsumerValidator) int64 { return sum } -func CapSatisfiable(vals []types.ConsumerValidator, percent uint32) bool { +func CapSatisfiable(vals []types.ConsensusValidator, 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) @@ -569,14 +570,14 @@ 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 { + capRespectedIfSatisfiable := func(valsBefore, valsAfter []types.ConsensusValidator, percent uint32) bool { if CapSatisfiable(valsBefore, percent) { return noMoreThanPercent(valsAfter, percent) } return true } - evenPowersIfCapCannotBeSatisfied := func(valsBefore, valsAfter []types.ConsumerValidator, percent uint32) bool { + evenPowersIfCapCannotBeSatisfied := func(valsBefore, valsAfter []types.ConsensusValidator, percent uint32) bool { if !CapSatisfiable(valsBefore, percent) { // if the cap cannot be satisfied, each validator should have the same power for _, valAfter := range valsAfter { @@ -590,7 +591,7 @@ func TestNoMoreThanPercentOfTheSumProps(t *testing.T) { // 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 { + fairness := func(valsBefore, valsAfter []types.ConsensusValidator) bool { for i, v := range valsBefore { // find the validator after with the same address vAfter := findConsumerValidator(t, v, valsAfter) @@ -619,7 +620,7 @@ func TestNoMoreThanPercentOfTheSumProps(t *testing.T) { } // non-zero: v has non-zero power before IFF it has non-zero power after - nonZero := func(valsBefore, valsAfter []types.ConsumerValidator) bool { + nonZero := func(valsBefore, valsAfter []types.ConsensusValidator) bool { for _, v := range valsBefore { vAfter := findConsumerValidator(t, v, valsAfter) if (v.Power == 0) != (vAfter.Power == 0) { @@ -631,7 +632,7 @@ func TestNoMoreThanPercentOfTheSumProps(t *testing.T) { // 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 { + equalSumIfCapSatisfiable := func(valsBefore, valsAfter []types.ConsensusValidator, percent uint32) bool { if CapSatisfiable(valsBefore, percent) { difference := gomath.Abs(float64(sumPowers(valsBefore) - sumPowers(valsAfter))) if difference > 1 { @@ -643,7 +644,7 @@ func TestNoMoreThanPercentOfTheSumProps(t *testing.T) { } // num validators: the number of validators will not change - equalNumVals := func(valsBefore, valsAfter []types.ConsumerValidator) bool { + equalNumVals := func(valsBefore, valsAfter []types.ConsensusValidator) bool { return len(valsBefore) == len(valsAfter) } @@ -669,8 +670,8 @@ func TestNoMoreThanPercentOfTheSumProps(t *testing.T) { }) } -func findConsumerValidator(t *testing.T, v types.ConsumerValidator, valsAfter []types.ConsumerValidator) *types.ConsumerValidator { - var vAfter *types.ConsumerValidator +func findConsumerValidator(t *testing.T, v types.ConsensusValidator, valsAfter []types.ConsensusValidator) *types.ConsensusValidator { + var vAfter *types.ConsensusValidator for _, vA := range valsAfter { if bytes.Equal(v.ProviderConsAddr, vA.ProviderConsAddr) { vAfter = &vA @@ -682,3 +683,124 @@ func findConsumerValidator(t *testing.T, v types.ConsumerValidator, valsAfter [] } return vAfter } + +func createStakingValidatorsAndMocks(ctx sdk.Context, mocks testkeeper.MockedKeepers, powers ...int64) ([]stakingtypes.Validator, []types.ProviderConsAddress) { + var validators []stakingtypes.Validator + for i, power := range powers { + val := createStakingValidator(ctx, mocks, i, power, i) + val.Tokens = math.NewInt(power) + val.Status = stakingtypes.Bonded + validators = append(validators, val) + } + + var consAddrs []types.ProviderConsAddress + for _, val := range validators { + consAddr, err := val.GetConsAddr() + if err != nil { + panic(err) + } + consAddrs = append(consAddrs, types.NewProviderConsAddress(consAddr)) + } + // set up mocks + for index, val := range validators { + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, consAddrs[index].Address).Return(val, nil).AnyTimes() + } + + return validators, consAddrs +} + +// TestFulfillsMinStake checks that FulfillsMinStake returns true if the validator has at least the min stake +// and false otherwise +func TestFulfillsMinStake(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // create two validators with powers 1 and 2 + _, consAddrs := createStakingValidatorsAndMocks(ctx, mocks, 1, 2) + + testCases := []struct { + name string + minStake uint64 + expectedFulfill []bool + }{ + { + name: "No min stake", + minStake: 0, + expectedFulfill: []bool{true, true}, + }, + { + name: "Min stake set to 2", + minStake: 2, + expectedFulfill: []bool{false, true}, + }, + { + name: "Min stake set to 3", + minStake: 3, + expectedFulfill: []bool{false, false}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + providerKeeper.SetMinStake(ctx, "chainID", tc.minStake) + for i, valAddr := range consAddrs { + result := providerKeeper.FulfillsMinStake(ctx, "chainID", valAddr) + require.Equal(t, tc.expectedFulfill[i], result) + } + }) + } +} + +// TestIfInactiveValsDisallowedProperty checks that the number of validators in the next validator set is at most +// the MaxProviderConsensusValidators parameter if the consumer chain does not allow inactive validators to validate. +func TestIfInactiveValsDisallowedProperty(t *testing.T) { + rapid.Check(t, func(r *rapid.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // Generate a random number of validators with random powers + valPowers := rapid.SliceOfN(rapid.Int64Range(1, 100), 1, 100).Draw(r, "valPowers") + vals, consAddrs := createStakingValidatorsAndMocks(ctx, mocks, valPowers...) + + // opt the validators in + for _, valAddr := range consAddrs { + providerKeeper.SetOptedIn(ctx, "chainID", valAddr) + } + + // Randomly choose values for parameters + minStake := rapid.Uint64Range(0, 101).Draw(r, "minStake") + maxProviderConsensusVals := rapid.Uint32Range(1, 11).Draw(r, "maxProviderConsensusVals") + + // Set up the parameters in the provider keeper + + // do not allow inactive validators + providerKeeper.SetInactiveValidatorsAllowed(ctx, "chainID", false) + providerKeeper.SetMinStake(ctx, "chainID", minStake) + params := providerKeeper.GetParams(ctx) + params.MaxProviderConsensusValidators = int64(maxProviderConsensusVals) + providerKeeper.SetParams(ctx, params) + + // Compute the next validators + nextVals := providerKeeper.ComputeNextValidators(ctx, "chainID", vals) + + // Check that the length of nextVals is at most maxProviderConsensusVals + require.LessOrEqual(r, len(nextVals), int(maxProviderConsensusVals), "The length of nextVals should be at most maxProviderConsensusVals") + + // Sanity check: we only get 0 next validators if either: + // - maxProviderConsensusVals is 0 + // - the maximal validator power is less than the min stake + if len(nextVals) == 0 { + maxValPower := int64(0) + for _, power := range valPowers { + if power > maxValPower { + maxValPower = power + } + } + require.True( + r, + maxProviderConsensusVals == 0 || maxValPower < int64(minStake), + "The length of nextVals should only be 0 if either maxProviderConsensusVals is 0 or the maximal validator power is less than the min stake", + ) + } + }) +} diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 52007a38e8..f67f597370 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -2,7 +2,6 @@ package keeper import ( "fmt" - "strconv" "time" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -45,6 +44,8 @@ func (k Keeper) HandleConsumerAdditionProposal(ctx sdk.Context, proposal *types. ValidatorSetCap: proposal.ValidatorSetCap, Allowlist: proposal.Allowlist, Denylist: proposal.Denylist, + MinStake: proposal.MinStake, + AllowInactiveVals: proposal.AllowInactiveVals, } return k.HandleLegacyConsumerAdditionProposal(ctx, &p) @@ -83,6 +84,8 @@ func (k Keeper) HandleConsumerModificationProposal(ctx sdk.Context, proposal *ty ValidatorSetCap: proposal.ValidatorSetCap, Allowlist: proposal.Allowlist, Denylist: proposal.Denylist, + MinStake: proposal.MinStake, + AllowInactiveVals: proposal.AllowInactiveVals, } return k.HandleLegacyConsumerModificationProposal(ctx, &p) @@ -141,10 +144,6 @@ func (k Keeper) CreateConsumerClient(ctx sdk.Context, prop *types.ConsumerAdditi } k.SetConsumerClientId(ctx, chainID, clientID) - // add the init timeout timestamp for this consumer chain - ts := ctx.BlockTime().Add(k.GetParams(ctx).InitTimeoutPeriod) - k.SetInitTimeoutTimestamp(ctx, chainID, uint64(ts.UnixNano())) - k.Logger(ctx).Info("consumer chain registered (client created)", "chainID", chainID, "clientID", clientID, @@ -157,7 +156,6 @@ func (k Keeper) CreateConsumerClient(ctx sdk.Context, prop *types.ConsumerAdditi sdk.NewAttribute(ccv.AttributeChainID, chainID), sdk.NewAttribute(clienttypes.AttributeKeyClientID, clientID), sdk.NewAttribute(types.AttributeInitialHeight, prop.InitialHeight.String()), - sdk.NewAttribute(types.AttributeInitializationTimeout, strconv.Itoa(int(ts.UnixNano()))), sdk.NewAttribute(types.AttributeTrustingPeriod, clientState.TrustingPeriod.String()), sdk.NewAttribute(types.AttributeUnbondingPeriod, clientState.UnbondingPeriod.String()), ), @@ -182,7 +180,6 @@ func (k Keeper) StopConsumerChain(ctx sdk.Context, chainID string, closeChan boo // clean up states k.DeleteConsumerClientId(ctx, chainID) k.DeleteConsumerGenesis(ctx, chainID) - k.DeleteInitTimeoutTimestamp(ctx, chainID) // Note: this call panics if the key assignment state is invalid k.DeleteKeyAssignments(ctx, chainID) k.DeleteMinimumPowerInTopN(ctx, chainID) @@ -207,9 +204,6 @@ func (k Keeper) StopConsumerChain(ctx sdk.Context, chainID string, closeChan boo } k.DeleteChainToChannel(ctx, chainID) k.DeleteChannelToChain(ctx, channelID) - - // delete VSC send timestamps - k.DeleteVscSendTimestampsForConsumer(ctx, chainID) } // delete consumer commission rate @@ -222,31 +216,13 @@ func (k Keeper) StopConsumerChain(ctx sdk.Context, chainID string, closeChan boo k.DeleteSlashAcks(ctx, chainID) k.DeletePendingVSCPackets(ctx, chainID) - // release unbonding operations - for _, unbondingOpsIndex := range k.GetAllUnbondingOpIndexes(ctx, chainID) { - // iterate over the unbonding operations for the current VSC ID - var maturedIds []uint64 - for _, id := range unbondingOpsIndex.UnbondingOpIds { - // Remove consumer chain ID from unbonding op record. - // Note that RemoveConsumerFromUnbondingOp cannot panic here - // as it is expected that for all UnbondingOpIds in every - // VscUnbondingOps returned by GetAllUnbondingOpIndexes - // there is an unbonding op in store that can be retrieved - // via via GetUnbondingOp. - if k.RemoveConsumerFromUnbondingOp(ctx, id, chainID) { - // Store id of matured unbonding op for later completion of unbonding in staking module - maturedIds = append(maturedIds, id) - } - } - k.AppendMaturedUnbondingOps(ctx, maturedIds) - 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.DeleteMinStake(ctx, chainID) + k.DisableInactiveValidators(ctx, chainID) k.DeleteAllOptedIn(ctx, chainID) k.DeleteConsumerValSet(ctx, chainID) @@ -293,20 +269,35 @@ func (k Keeper) MakeConsumerGenesis( } if prop.Top_N > 0 { + // get the consensus active validators + // we do not want to base the power calculation for the top N + // on inactive validators, too, since the top N will be a percentage of the active set power + // otherwise, it could be that inactive validators are forced to validate + activeValidators, err := k.GetLastProviderConsensusActiveValidators(ctx) + if err != nil { + return gen, nil, errorsmod.Wrapf(stakingtypes.ErrNoValidatorFound, "error getting last active bonded validators: %s", err) + } + // in a Top-N chain, we automatically opt in all validators that belong to the top N - minPower, err := k.ComputeMinPowerInTopN(ctx, bondedValidators, prop.Top_N) + minPower, err := k.ComputeMinPowerInTopN(ctx, activeValidators, prop.Top_N) if err != nil { return gen, nil, err } - k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) + // log the minimum power in top N + k.Logger(ctx).Info("minimum power in top N at consumer genesis", + "chainID", chainID, + "minPower", minPower, + ) + k.OptInTopNValidators(ctx, chainID, activeValidators, minPower) k.SetMinimumPowerInTopN(ctx, chainID, minPower) } + // need to use the bondedValidators, not activeValidators, here since the chain might be opt-in and allow inactive vals nextValidators := k.ComputeNextValidators(ctx, chainID, bondedValidators) k.SetConsumerValSet(ctx, chainID, nextValidators) // get the initial updates with the latest set consumer public keys - initialUpdatesWithConsumerKeys := DiffValidators([]types.ConsumerValidator{}, nextValidators) + initialUpdatesWithConsumerKeys := DiffValidators([]types.ConsensusValidator{}, nextValidators) // Get a hash of the consumer validator set from the update with applied consumer assigned keys updatesAsValSet, err := tmtypes.PB2TM.ValidatorUpdates(initialUpdatesWithConsumerKeys) @@ -342,7 +333,7 @@ func (k Keeper) MakeConsumerGenesis( // SetPendingConsumerAdditionProp stores a pending consumer addition proposal. // // Note that the pending consumer addition proposals are stored under keys with -// the following format: PendingCAPBytePrefix | spawnTime | chainID +// the following format: PendingCAPKeyPrefix | spawnTime | chainID // Thus, if multiple consumer addition proposal for the same chain will pass at // the same time, then only the last one will be stored. func (k Keeper) SetPendingConsumerAdditionProp(ctx sdk.Context, prop *types.ConsumerAdditionProposal) { @@ -392,6 +383,8 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { k.SetTopN(cachedCtx, prop.ChainId, prop.Top_N) k.SetValidatorSetCap(cachedCtx, prop.ChainId, prop.ValidatorSetCap) k.SetValidatorsPowerCap(cachedCtx, prop.ChainId, prop.ValidatorsPowerCap) + k.SetMinStake(cachedCtx, prop.ChainId, prop.MinStake) + k.SetInactiveValidatorsAllowed(cachedCtx, prop.ChainId, prop.AllowInactiveVals) for _, address := range prop.Allowlist { consAddr, err := sdk.ConsAddressFromBech32(address) @@ -454,7 +447,7 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { // Note: this method is split out from BeginBlockInit to be easily unit tested. func (k Keeper) GetConsumerAdditionPropsToExecute(ctx sdk.Context) (propsToExecute []types.ConsumerAdditionProposal) { store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.PendingCAPBytePrefix}) + iterator := storetypes.KVStorePrefixIterator(store, types.PendingCAPKeyPrefix()) defer iterator.Close() @@ -480,12 +473,12 @@ func (k Keeper) GetConsumerAdditionPropsToExecute(ctx sdk.Context) (propsToExecu // GetAllPendingConsumerAdditionProps gets all pending consumer addition proposals. // // Note that the pending consumer addition proposals are stored under keys with the following format: -// PendingCAPBytePrefix | spawnTime.UnixNano() | chainID +// PendingCAPKeyPrefix | spawnTime.UnixNano() | chainID // Thus, the returned array is in spawnTime order. If two proposals have the same spawnTime, // then they are ordered by chainID. func (k Keeper) GetAllPendingConsumerAdditionProps(ctx sdk.Context) (props []types.ConsumerAdditionProposal) { store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.PendingCAPBytePrefix}) + iterator := storetypes.KVStorePrefixIterator(store, types.PendingCAPKeyPrefix()) defer iterator.Close() @@ -516,7 +509,7 @@ func (k Keeper) DeletePendingConsumerAdditionProps(ctx sdk.Context, proposals .. // SetPendingConsumerRemovalProp stores a pending consumer removal proposal. // // Note that the pending removal addition proposals are stored under keys with -// the following format: PendingCRPBytePrefix | stopTime | chainID +// the following format: PendingCRPKeyPrefix | stopTime | chainID // Thus, if multiple removal addition proposal for the same chain will pass at // the same time, then only the last one will be stored. func (k Keeper) SetPendingConsumerRemovalProp(ctx sdk.Context, prop *types.ConsumerRemovalProposal) { @@ -594,7 +587,7 @@ func (k Keeper) GetConsumerRemovalPropsToExecute(ctx sdk.Context) []types.Consum propsToExecute := []types.ConsumerRemovalProposal{} store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.PendingCRPBytePrefix}) + iterator := storetypes.KVStorePrefixIterator(store, types.PendingCRPKeyPrefix()) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { @@ -621,11 +614,11 @@ func (k Keeper) GetConsumerRemovalPropsToExecute(ctx sdk.Context) []types.Consum // GetAllPendingConsumerRemovalProps iterates through the pending consumer removal proposals. // // Note that the pending consumer removal proposals are stored under keys with the following format: -// PendingCRPBytePrefix | stopTime.UnixNano() | chainID +// PendingCRPKeyPrefix | stopTime.UnixNano() | chainID // Thus, the returned array is in stopTime order. func (k Keeper) GetAllPendingConsumerRemovalProps(ctx sdk.Context) (props []types.ConsumerRemovalProposal) { store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.PendingCRPBytePrefix}) + iterator := storetypes.KVStorePrefixIterator(store, types.PendingCRPKeyPrefix()) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index 8cb9f3a00d..472cb3c64c 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -72,6 +72,8 @@ func TestHandleConsumerAdditionProposal(t *testing.T) { 0, nil, nil, + 0, + false, ).(*providertypes.ConsumerAdditionProposal), blockTime: now, expAppendProp: true, @@ -102,6 +104,8 @@ func TestHandleConsumerAdditionProposal(t *testing.T) { 0, nil, nil, + 0, + false, ).(*providertypes.ConsumerAdditionProposal), blockTime: now, expAppendProp: false, @@ -117,7 +121,7 @@ func TestHandleConsumerAdditionProposal(t *testing.T) { if tc.expAppendProp { // Mock calls are only asserted if we expect a client to be created. - testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, []int64{}, 1) // returns empty validator set + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, 1) // returns empty validator set gomock.InOrder( testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, tc.prop.ChainId, clienttypes.NewHeight(2, 3))..., ) @@ -161,7 +165,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 - testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, []int64{}, 1) // returns empty validator set + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, 1) // returns empty validator set gomock.InOrder( testkeeper.GetMocksForCreateConsumerClient(ctx, mocks, "chainID", clienttypes.NewHeight(4, 5))..., ) @@ -177,7 +181,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) - testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, []int64{}, 0) // returns empty validator set + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, 0) // returns empty validator set }, expClientCreated: false, }, @@ -650,8 +654,6 @@ func TestMakeConsumerGenesis(t *testing.T) { // They must be populated with reasonable values to satisfy SetParams though. TrustingPeriodFraction: providertypes.DefaultTrustingPeriodFraction, CcvTimeoutPeriod: ccvtypes.DefaultCCVTimeoutPeriod, - InitTimeoutPeriod: providertypes.DefaultInitTimeoutPeriod, - VscTimeoutPeriod: providertypes.DefaultVscTimeoutPeriod, SlashMeterReplenishPeriod: providertypes.DefaultSlashMeterReplenishPeriod, SlashMeterReplenishFraction: providertypes.DefaultSlashMeterReplenishFraction, ConsumerRewardDenomRegistrationFee: sdk.Coin{ @@ -669,7 +671,7 @@ func TestMakeConsumerGenesis(t *testing.T) { // ctx = ctx.WithChainID("testchain1") // chainID is obtained from ctx ctx = ctx.WithBlockHeight(5) // RevisionHeight obtained from ctx - testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, []int64{}, 1) + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, 1) gomock.InOrder(testkeeper.GetMocksForMakeConsumerGenesis(ctx, &mocks, 1814400000000000)...) // matches params from jsonString @@ -814,6 +816,8 @@ func TestBeginBlockInit(t *testing.T) { 0, nil, nil, + 0, + false, ).(*providertypes.ConsumerAdditionProposal), providertypes.NewConsumerAdditionProposal( "title", "spawn time passed", "chain2", clienttypes.NewHeight(3, 4), []byte{}, []byte{}, @@ -830,6 +834,8 @@ func TestBeginBlockInit(t *testing.T) { 0, nil, nil, + 0, + false, ).(*providertypes.ConsumerAdditionProposal), providertypes.NewConsumerAdditionProposal( "title", "spawn time not passed", "chain3", clienttypes.NewHeight(3, 4), []byte{}, []byte{}, @@ -846,6 +852,8 @@ func TestBeginBlockInit(t *testing.T) { 0, nil, nil, + 0, + false, ).(*providertypes.ConsumerAdditionProposal), providertypes.NewConsumerAdditionProposal( "title", "invalid proposal: chain id already exists", "chain2", clienttypes.NewHeight(4, 5), []byte{}, []byte{}, @@ -862,6 +870,8 @@ func TestBeginBlockInit(t *testing.T) { 0, nil, nil, + 0, + false, ).(*providertypes.ConsumerAdditionProposal), providertypes.NewConsumerAdditionProposal( "title", "opt-in chain with at least one validator opted in", "chain5", clienttypes.NewHeight(3, 4), []byte{}, []byte{}, @@ -878,6 +888,8 @@ func TestBeginBlockInit(t *testing.T) { 0, nil, nil, + 0, + false, ).(*providertypes.ConsumerAdditionProposal), providertypes.NewConsumerAdditionProposal( "title", "opt-in chain with no validator opted in", "chain6", clienttypes.NewHeight(3, 4), []byte{}, []byte{}, @@ -894,6 +906,8 @@ func TestBeginBlockInit(t *testing.T) { 0, nil, nil, + 0, + false, ).(*providertypes.ConsumerAdditionProposal), } @@ -915,10 +929,14 @@ 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() - testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 1, []stakingtypes.Validator{validator}, []int64{0}, -1) // -1 to allow any number of calls + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 1, []stakingtypes.Validator{validator}, -1) // -1 to allow any number of calls valAddr, _ := sdk.ValAddressFromBech32(validator.GetOperator()) mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower(gomock.Any(), valAddr).Return(int64(1), nil).AnyTimes() + + // for the validator, expect a call to GetValidatorByConsAddr with its consensus address + mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(gomock.Any(), consAddr).Return(validator, nil).AnyTimes() + providerKeeper.SetOptedIn(ctx, pendingProps[4].ChainId, providertypes.NewProviderConsAddress(consAddr)) providerKeeper.BeginBlockInit(ctx) @@ -1023,7 +1041,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 - testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, []int64{}, 1) + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, 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/provider_consensus.go b/x/ccv/provider/keeper/provider_consensus.go new file mode 100644 index 0000000000..7bd1e44576 --- /dev/null +++ b/x/ccv/provider/keeper/provider_consensus.go @@ -0,0 +1,87 @@ +package keeper + +import ( + "fmt" + + "cosmossdk.io/math" + + 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" +) + +// SetLastProviderConsensusValidator sets the given validator to be stored +// as part of the last provider consensus validator set +func (k Keeper) SetLastProviderConsensusValidator( + ctx sdk.Context, + validator types.ConsensusValidator, +) { + k.setValidator(ctx, types.LastProviderConsensusValsPrefix(), validator) +} + +// SetLastProviderConsensusValSet resets the stored last validator set sent to the consensus engine on the provider +// to the provided `nextValidators`. +func (k Keeper) SetLastProviderConsensusValSet(ctx sdk.Context, nextValidators []types.ConsensusValidator) { + k.setValSet(ctx, types.LastProviderConsensusValsPrefix(), nextValidators) +} + +// DeleteLastProviderConsensusValidator removes the validator with `providerConsAddr` address +// from the stored last provider consensus validator set +func (k Keeper) DeleteLastProviderConsensusValidator( + ctx sdk.Context, + providerConsAddr types.ProviderConsAddress, +) { + k.deleteValidator(ctx, types.LastProviderConsensusValsPrefix(), providerConsAddr) +} + +// DeleteLastProviderConsensusValSet deletes all the stored validators from the +// last provider consensus validator set +func (k Keeper) DeleteLastProviderConsensusValSet( + ctx sdk.Context, +) { + k.deleteValSet(ctx, types.LastProviderConsensusValsPrefix()) +} + +// GetLastProviderConsensusValSet returns the last stored +// validator set sent to the consensus engine on the provider +func (k Keeper) GetLastProviderConsensusValSet( + ctx sdk.Context, +) ([]types.ConsensusValidator, error) { + return k.getValSet(ctx, types.LastProviderConsensusValsPrefix()) +} + +// GetLastTotalProviderConsensusPower returns the total power of the last stored +// validator set sent to the consensus engine on the provider +func (k Keeper) GetLastTotalProviderConsensusPower( + ctx sdk.Context, +) (math.Int, error) { + return k.getTotalPower(ctx, types.LastProviderConsensusValsPrefix()) +} + +// CreateProviderConsensusValidator creates a new ConsensusValidator from the given staking validator +func (k Keeper) CreateProviderConsensusValidator(ctx sdk.Context, val stakingtypes.Validator) (types.ConsensusValidator, error) { + consAddr, err := val.GetConsAddr() + if err != nil { + return types.ConsensusValidator{}, fmt.Errorf("getting consensus address: %w", err) + } + pubKey, err := val.TmConsPublicKey() + if err != nil { + return types.ConsensusValidator{}, fmt.Errorf("getting consensus public key: %w", err) + } + valAddr, err := sdk.ValAddressFromBech32(val.GetOperator()) + if err != nil { + return types.ConsensusValidator{}, fmt.Errorf("getting validator address: %w", err) + } + + power, err := k.stakingKeeper.GetLastValidatorPower(ctx, valAddr) + if err != nil { + return types.ConsensusValidator{}, fmt.Errorf("getting validator power: %w", err) + } + + return types.ConsensusValidator{ + ProviderConsAddr: consAddr, + PublicKey: &pubKey, + Power: power, + }, nil +} diff --git a/x/ccv/provider/keeper/provider_consensus_test.go b/x/ccv/provider/keeper/provider_consensus_test.go new file mode 100644 index 0000000000..93a5c6f0ba --- /dev/null +++ b/x/ccv/provider/keeper/provider_consensus_test.go @@ -0,0 +1,135 @@ +package keeper_test + +import ( + "testing" + + "cosmossdk.io/math" + "github.com/cometbft/cometbft/proto/tendermint/crypto" + "github.com/stretchr/testify/require" + + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" +) + +func TestSetLastProviderConsensusValidator(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + validator := types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddr"), + Power: 2, + PublicKey: &crypto.PublicKey{}, + } + + providerKeeper.SetLastProviderConsensusValidator(ctx, validator) + + // Retrieve the stored validator + vals, err := providerKeeper.GetLastProviderConsensusValSet(ctx) + require.NoError(t, err, "failed to get stored validator") + + storedValidator := vals[0] + require.Equal(t, validator, storedValidator, "stored validator does not match") +} + +func TestSetLastProviderConsensusValSet(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + validator1 := types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddr1"), + Power: 2, + PublicKey: &crypto.PublicKey{}, + } + + validator2 := types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddr2"), + Power: 3, + PublicKey: &crypto.PublicKey{}, + } + + nextValidators := []types.ConsensusValidator{validator1, validator2} + + providerKeeper.SetLastProviderConsensusValSet(ctx, nextValidators) + + // Retrieve the stored validator set + storedValidators, err := providerKeeper.GetLastProviderConsensusValSet(ctx) + require.NoError(t, err, "failed to get stored validator set") + require.Equal(t, nextValidators, storedValidators, "stored validator set does not match") +} + +func TestDeleteLastProviderConsensusValidator(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + validator := types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddr"), + Power: 2, + PublicKey: &crypto.PublicKey{}, + } + + providerKeeper.SetLastProviderConsensusValidator(ctx, validator) + + // Delete the stored validator + providerKeeper.DeleteLastProviderConsensusValidator(ctx, types.NewProviderConsAddress(validator.ProviderConsAddr)) + + // Ensure the validator is deleted + storedValidators, err := providerKeeper.GetLastProviderConsensusValSet(ctx) + require.NoError(t, err, "failed to get stored validator set") + require.Empty(t, storedValidators, "validator set should be empty") +} + +func TestDeleteLastProviderConsensusValSet(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + validator1 := types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddr1"), + Power: 2, + PublicKey: &crypto.PublicKey{}, + } + + validator2 := types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddr2"), + Power: 3, + PublicKey: &crypto.PublicKey{}, + } + + nextValidators := []types.ConsensusValidator{validator1, validator2} + + providerKeeper.SetLastProviderConsensusValSet(ctx, nextValidators) + + // check that the set is not empty + storedValidators, err := providerKeeper.GetLastProviderConsensusValSet(ctx) + require.NoError(t, err, "failed to get stored validator set") + require.NotEmpty(t, storedValidators, "validator set should not be empty") + + // Delete the stored validator set + providerKeeper.DeleteLastProviderConsensusValSet(ctx) + + // Ensure the validator set is empty + storedValidators, err = providerKeeper.GetLastProviderConsensusValSet(ctx) + require.NoError(t, err, "failed to get stored validator set") + require.Empty(t, storedValidators, "validator set should be empty") +} + +func TestGetLastTotalProviderConsensusPower(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + validator1 := types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddr1"), + Power: 2, + PublicKey: &crypto.PublicKey{}, + } + validator2 := types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddr2"), + Power: 3, + PublicKey: &crypto.PublicKey{}, + } + nextValidators := []types.ConsensusValidator{validator1, validator2} + providerKeeper.SetLastProviderConsensusValSet(ctx, nextValidators) + // Get the total power of the last stored validator set + totalPower, err := providerKeeper.GetLastTotalProviderConsensusPower(ctx) + require.NoError(t, err, "failed to get total power") + expectedTotalPower := math.NewInt(5) + require.Equal(t, expectedTotalPower, totalPower, "total power does not match") +} diff --git a/x/ccv/provider/keeper/relay.go b/x/ccv/provider/keeper/relay.go index 4d8f862c1b..c95bd71079 100644 --- a/x/ccv/provider/keeper/relay.go +++ b/x/ccv/provider/keeper/relay.go @@ -13,100 +13,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + abci "github.com/cometbft/cometbft/abci/types" + + "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" ) -// OnRecvVSCMaturedPacket handles a VSCMatured packet and returns a no-op result ack. -func (k Keeper) OnRecvVSCMaturedPacket( - ctx sdk.Context, - packet channeltypes.Packet, - data ccv.VSCMaturedPacketData, -) error { - // check that the channel is established, panic if not - chainID, found := k.GetChannelToChain(ctx, packet.DestinationChannel) - if !found { - // VSCMatured packet was sent on a channel different than any of the established CCV channels; - // this should never happen - k.Logger(ctx).Error("VSCMaturedPacket received on unknown channel", - "channelID", packet.DestinationChannel, - ) - panic(fmt.Errorf("VSCMaturedPacket received on unknown channel %s", packet.DestinationChannel)) - } - - // validate packet data upon receiving - if err := data.Validate(); err != nil { - return errorsmod.Wrapf(err, "error validating VSCMaturedPacket data") - } - - k.HandleVSCMaturedPacket(ctx, chainID, data) - - k.Logger(ctx).Info("VSCMaturedPacket handled", - "chainID", chainID, - "vscID", data.ValsetUpdateId, - ) - - return nil -} - -// HandleVSCMaturedPacket handles a VSCMatured packet. -// -// Note: This method should only panic for a system critical error like a -// failed marshal/unmarshal, or persistence of critical data. -func (k Keeper) HandleVSCMaturedPacket(ctx sdk.Context, chainID string, data ccv.VSCMaturedPacketData) { - // iterate over the unbonding operations mapped to (chainID, data.ValsetUpdateId) - var maturedIds []uint64 - for _, unbondingOp := range k.GetUnbondingOpsFromIndex(ctx, chainID, data.ValsetUpdateId) { - // Remove consumer chain ID from unbonding op record. - // Note that RemoveConsumerFromUnbondingOp cannot panic here - // as all the unbonding ops returned by GetUnbondingOpsFromIndex - // are retrieved via GetUnbondingOp. - if k.RemoveConsumerFromUnbondingOp(ctx, unbondingOp.Id, chainID) { - // Store id of matured unbonding op for later completion of unbonding in staking module - maturedIds = append(maturedIds, unbondingOp.Id) - } - } - k.AppendMaturedUnbondingOps(ctx, maturedIds) - - // clean up index - k.DeleteUnbondingOpIndex(ctx, chainID, data.ValsetUpdateId) - - // remove the VSC timeout timestamp for this chainID and vscID - k.DeleteVscSendTimestamp(ctx, chainID, data.ValsetUpdateId) - - // prune previous consumer validator address that are no longer needed - k.PruneKeyAssignments(ctx, chainID, data.ValsetUpdateId) - - k.Logger(ctx).Info("VSCMaturedPacket handled", - "chainID", chainID, - "vscID", data.ValsetUpdateId, - ) -} - -// CompleteMaturedUnbondingOps attempts to complete all matured unbonding operations -func (k Keeper) completeMaturedUnbondingOps(ctx sdk.Context) { - for _, id := range k.ConsumeMaturedUnbondingOps(ctx) { - // Attempt to complete unbonding in staking module - err := k.stakingKeeper.UnbondingCanComplete(ctx, id) - if err != nil { - if errors.Is(err, stakingtypes.ErrNoUnbondingDelegation) { - // The unbonding was not found. - unbondingType, errGet := k.stakingKeeper.GetUnbondingType(ctx, id) - if errGet == nil && unbondingType == stakingtypes.UnbondingType_UnbondingDelegation { - // If this is an unbonding delegation, it may have been removed - // after through a CancelUnbondingDelegation message - k.Logger(ctx).Debug("unbonding delegation was already removed:", "unbondingID", id) - continue - } - } - // UnbondingCanComplete failing means that the state of the x/staking module - // of cosmos-sdk is invalid. An exception is the case handled above - panic(fmt.Sprintf("could not complete unbonding op: %s", err.Error())) - } - k.Logger(ctx).Debug("unbonding operation matured on all consumers", "opID", id) - } -} - // OnAcknowledgementPacket handles acknowledgments for sent VSC packets func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, ack channeltypes.Acknowledgement) error { if err := ack.GetError(); err != "" { @@ -145,11 +58,11 @@ func (k Keeper) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet) err // EndBlockVSU contains the EndBlock logic needed for // the Validator Set Update sub-protocol -func (k Keeper) EndBlockVSU(ctx sdk.Context) { - // notify the staking module to complete all matured unbonding ops - k.completeMaturedUnbondingOps(ctx) +func (k Keeper) EndBlockVSU(ctx sdk.Context) ([]abci.ValidatorUpdate, error) { + // logic to update the provider consensus validator set. + valUpdates := k.ProviderValidatorUpdates(ctx) - if ctx.BlockHeight()%k.GetBlocksPerEpoch(ctx) == 0 { + if k.BlocksUntilNextEpoch(ctx) == 0 { // only queue and send VSCPackets at the boundaries of an epoch // collect validator updates @@ -160,6 +73,62 @@ func (k Keeper) EndBlockVSU(ctx sdk.Context) { // the updates will remain queued until the channel is established k.SendVSCPackets(ctx) } + + return valUpdates, nil +} + +// ProviderValidatorUpdates returns changes in the provider consensus validator set +// from the last block to the current one. +// It retrieves the bonded validators from the staking module and creates a `ConsumerValidator` object for each validator. +// The maximum number of validators is determined by the `maxValidators` parameter. +// The function returns the difference between the current validator set and the next validator set as a list of `abci.ValidatorUpdate` objects. +func (k Keeper) ProviderValidatorUpdates(ctx sdk.Context) []abci.ValidatorUpdate { + // get the bonded validators from the staking module + bondedValidators, err := k.stakingKeeper.GetBondedValidatorsByPower(ctx) + if err != nil { + panic(fmt.Errorf("failed to get bonded validators: %w", err)) + } + + // get the last validator set sent to consensus + currentValidators, err := k.GetLastProviderConsensusValSet(ctx) + if err != nil { + panic(fmt.Errorf("failed to get last provider consensus validator set: %w", err)) + } + + nextValidators := []types.ConsensusValidator{} + maxValidators := k.GetMaxProviderConsensusValidators(ctx) + // avoid out of range errors by bounding the max validators to the number of bonded validators + if maxValidators > int64(len(bondedValidators)) { + maxValidators = int64(len(bondedValidators)) + } + for _, val := range bondedValidators[:maxValidators] { + nextValidator, err := k.CreateProviderConsensusValidator(ctx, val) + if err != nil { + k.Logger(ctx).Error("error when creating provider consensus validator", "error", err, "validator", val) + continue + } + nextValidators = append(nextValidators, nextValidator) + } + + // store the validator set we will send to consensus + k.SetLastProviderConsensusValSet(ctx, nextValidators) + + valUpdates := DiffValidators(currentValidators, nextValidators) + + return valUpdates +} + +// BlocksUntilNextEpoch returns the number of blocks until the next epoch starts +// Returns 0 if VSCPackets are sent in the current block, +// which is done in the first block of each epoch. +func (k Keeper) BlocksUntilNextEpoch(ctx sdk.Context) int64 { + blocksSinceEpochStart := ctx.BlockHeight() % k.GetBlocksPerEpoch(ctx) + + if blocksSinceEpochStart == 0 { + return 0 + } else { + return int64(k.GetBlocksPerEpoch(ctx) - blocksSinceEpochStart) + } } // SendVSCPackets iterates over all registered consumers and sends pending @@ -206,10 +175,6 @@ func (k Keeper) SendVSCPacketsToChain(ctx sdk.Context, chainID, channelID string } return } - // set the VSC send timestamp for this packet; - // note that the VSC send timestamp are set when the packets - // are actually sent over IBC - k.SetVscSendTimestamp(ctx, chainID, data.ValsetUpdateId, ctx.BlockTime()) } k.DeletePendingVSCPackets(ctx, chainID) } @@ -228,21 +193,31 @@ func (k Keeper) QueueVSCPackets(ctx sdk.Context) { } for _, chainID := range k.GetAllRegisteredConsumerChainIDs(ctx) { - currentValidators := k.GetConsumerValSet(ctx, chainID) + currentValidators, err := k.GetConsumerValSet(ctx, chainID) + if err != nil { + panic(fmt.Errorf("failed to get consumer validators: %w", err)) + } topN, _ := k.GetTopN(ctx, chainID) if topN > 0 { // in a Top-N chain, we automatically opt in all validators that belong to the top N - 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 - k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err) + // of the active validators + activeValidators, err := k.GetLastProviderConsensusActiveValidators(ctx) + if err != nil { + // something must be broken in the bonded validators, so we have to panic since there is no realistic way to proceed + panic(fmt.Errorf("failed to get active validators: %w", err)) + } + + minPower, err := k.ComputeMinPowerInTopN(ctx, activeValidators, topN) + if err != nil { + // we panic, since the only way to proceed would be to opt in all validators, which is not the intended behavior + panic(fmt.Errorf("failed to compute min power to opt in for chain %v: %w", chainID, err)) } + + // set the minimal power of validators in the top N in the store + k.SetMinimumPowerInTopN(ctx, chainID, minPower) + + k.OptInTopNValidators(ctx, chainID, activeValidators, minPower) } nextValidators := k.ComputeNextValidators(ctx, chainID, bondedValidators) @@ -250,11 +225,8 @@ func (k Keeper) QueueVSCPackets(ctx sdk.Context) { valUpdates := DiffValidators(currentValidators, 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, chainID, valUpdateID) - if len(valUpdates) != 0 || len(unbondingOps) != 0 { + // check whether there are changes in the validator set + if len(valUpdates) != 0 { // construct validator set change packet data packet := ccv.NewValidatorSetChangePacketData(valUpdates, valUpdateID, k.ConsumeSlashAcks(ctx, chainID)) k.AppendPendingVSCPackets(ctx, chainID, packet) @@ -262,7 +234,6 @@ func (k Keeper) QueueVSCPackets(ctx sdk.Context) { "chainID", chainID, "vscID", valUpdateID, "len updates", len(valUpdates), - "len unbonding ops", len(unbondingOps), ) } } @@ -293,6 +264,11 @@ func (k Keeper) EndBlockCIS(ctx sdk.Context) { valUpdateID := k.GetValidatorSetUpdateId(ctx) k.SetValsetUpdateBlockHeight(ctx, valUpdateID, blockHeight) k.Logger(ctx).Debug("vscID was mapped to block height", "vscID", valUpdateID, "height", blockHeight) + + // prune previous consumer validator addresses that are no longer needed + for _, chainID := range k.GetAllRegisteredConsumerChainIDs(ctx) { + k.PruneKeyAssignments(ctx, chainID) + } } // OnRecvSlashPacket delivers a received slash packet, validates it and @@ -417,7 +393,7 @@ func (k Keeper) HandleSlashPacket(ctx sdk.Context, chainID string, data ccv.Slas // Obtain provider chain consensus address using the consumer chain consensus address providerConsAddr := k.GetProviderAddrFromConsumerAddr(ctx, chainID, consumerConsAddr) - k.Logger(ctx).Debug("handling slash packet", + k.Logger(ctx).Debug("HandleSlashPacket", "chainID", chainID, "consumer cons addr", consumerConsAddr.String(), "provider cons addr", providerConsAddr.String(), @@ -436,10 +412,10 @@ func (k Keeper) HandleSlashPacket(ctx sdk.Context, chainID string, data ccv.Slas // stakingKeeper.Slash() panics otherwise if validator.IsUnbonded() { // if validator is not found or is unbonded, drop slash packet and log error. - // Note that it is impossible for the validator to be not found or unbonded if both the provider - // and the consumer are following the protocol. Thus if this branch is taken then one or both - // chains is incorrect, but it is impossible to tell which. - k.Logger(ctx).Error("validator already unbonded", "validator", providerConsAddr.String()) + k.Logger(ctx).Info( + "HandleSlashPacket - slash packet dropped because validator not found or is unbonded", + "provider cons addr", providerConsAddr.String(), + ) return } @@ -447,7 +423,7 @@ func (k Keeper) HandleSlashPacket(ctx sdk.Context, chainID string, data ccv.Slas if k.slashingKeeper.IsTombstoned(ctx, providerConsAddr.ToSdkConsAddr()) { // Log and drop packet if validator is tombstoned. k.Logger(ctx).Info( - "slash packet dropped because validator is already tombstoned", + "HandleSlashPacket - slash packet dropped because validator is already tombstoned", "provider cons addr", providerConsAddr.String(), ) return @@ -455,7 +431,10 @@ func (k Keeper) HandleSlashPacket(ctx sdk.Context, chainID string, data ccv.Slas infractionHeight, found := k.getMappedInfractionHeight(ctx, chainID, data.ValsetUpdateId) if !found { - k.Logger(ctx).Error("infraction height not found. But was found during slash packet validation") + k.Logger(ctx).Error( + "HandleSlashPacket - infraction height not found. But was found during slash packet validation", + "vscID", data.ValsetUpdateId, + ) // drop packet return } @@ -474,7 +453,7 @@ func (k Keeper) HandleSlashPacket(ctx sdk.Context, chainID string, data ccv.Slas k.Logger(ctx).Error("failed to jail vaidator", providerConsAddr.ToSdkConsAddr().String(), "err", err.Error()) return } - k.Logger(ctx).Info("validator jailed", "provider cons addr", providerConsAddr.String()) + k.Logger(ctx).Info("HandleSlashPacket - validator jailed", "provider cons addr", providerConsAddr.String()) jailDuration, err := k.slashingKeeper.DowntimeJailDuration(ctx) if err != nil { k.Logger(ctx).Error("failed to get jail duration", "err", err.Error()) @@ -500,60 +479,6 @@ func (k Keeper) HandleSlashPacket(ctx sdk.Context, chainID string, data ccv.Slas ) } -// EndBlockCCR contains the EndBlock logic needed for -// the Consumer Chain Removal sub-protocol -func (k Keeper) EndBlockCCR(ctx sdk.Context) { - currentTime := ctx.BlockTime() - currentTimeUint64 := uint64(currentTime.UnixNano()) - - for _, initTimeoutTimestamp := range k.GetAllInitTimeoutTimestamps(ctx) { - if currentTimeUint64 > initTimeoutTimestamp.Timestamp { - // initTimeout expired - // stop the consumer chain and unlock the unbonding. - // Note that the CCV channel was not established, - // thus closeChan is irrelevant - k.Logger(ctx).Info("about to remove timed out consumer chain - chain was not initialised", - "chainID", initTimeoutTimestamp.ChainId) - err := k.StopConsumerChain(ctx, initTimeoutTimestamp.ChainId, false) - if err != nil { - if errors.Is(err, providertypes.ErrConsumerChainNotFound) { - // consumer chain not found - continue - } - panic(fmt.Errorf("consumer chain failed to stop: %w", err)) - } - } - } - - for _, channelToChain := range k.GetAllChannelToChains(ctx) { - // Check if the first vscSendTimestamp in iterator + VscTimeoutPeriod - // exceed the current block time. - // Checking the first send timestamp for each chain is sufficient since - // timestamps are ordered by vsc ID. - // Note: GetFirstVscSendTimestamp panics if the internal state is invalid - vscSendTimestamp, found := k.GetFirstVscSendTimestamp(ctx, channelToChain.ChainId) - if found { - timeoutTimestamp := vscSendTimestamp.Timestamp.Add(k.GetParams(ctx).VscTimeoutPeriod) - if currentTime.After(timeoutTimestamp) { - // vscTimeout expired - // stop the consumer chain and release unbondings - k.Logger(ctx).Info("about to remove timed out consumer chain - VSCPacket timed out", - "chainID", channelToChain.ChainId, - "vscID", vscSendTimestamp.VscId, - ) - err := k.StopConsumerChain(ctx, channelToChain.ChainId, true) - if err != nil { - if errors.Is(err, providertypes.ErrConsumerChainNotFound) { - // consumer chain not found - continue - } - panic(fmt.Errorf("consumer chain failed to stop: %w", err)) - } - } - } - } -} - // getMappedInfractionHeight gets the infraction height mapped from val set ID for the given chain ID func (k Keeper) getMappedInfractionHeight(ctx sdk.Context, chainID string, valsetUpdateID uint64, diff --git a/x/ccv/provider/keeper/relay_test.go b/x/ccv/provider/keeper/relay_test.go index 8f3730def5..91de369851 100644 --- a/x/ccv/provider/keeper/relay_test.go +++ b/x/ccv/provider/keeper/relay_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "sort" "strings" "testing" @@ -11,15 +12,12 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" - 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" - "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" @@ -68,7 +66,7 @@ func TestQueueVSCPackets(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mocks := testkeeper.NewMockedKeepers(ctrl) - testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, []int64{}, 1) + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 0, []stakingtypes.Validator{}, 1) pk := testkeeper.NewInMemProviderKeeper(keeperParams, mocks) // no-op if tc.packets is empty @@ -103,18 +101,18 @@ func TestQueueVSCPacketsDoesNotResetConsumerValidatorsHeights(t *testing.T) { valB := createStakingValidator(ctx, mocks, 2, 2, 2) valBConsAddr, _ := valB.GetConsAddr() mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valBConsAddr).Return(valB, nil).AnyTimes() - testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 2, []stakingtypes.Validator{valA, valB}, []int64{1, 2}, -1) + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 2, []stakingtypes.Validator{valA, valB}, -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 := providertypes.ConsumerValidator{ - ProviderConsAddr: valAConsAddr, - Power: 1, - ConsumerPublicKey: &valAPubKey, - JoinHeight: 123456789, + consumerValidatorA := providertypes.ConsensusValidator{ + ProviderConsAddr: valAConsAddr, + Power: 1, + PublicKey: &valAPubKey, + JoinHeight: 123456789, } providerKeeper.SetConsumerValidator(ctx, "chainID", consumerValidatorA) @@ -134,27 +132,6 @@ func TestQueueVSCPacketsDoesNotResetConsumerValidatorsHeights(t *testing.T) { 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. -func TestOnRecvVSCMaturedPacket(t *testing.T) { - providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - providerKeeper.SetParams(ctx, providertypes.DefaultParams()) - - // Set channel to chain (faking multiple established channels) - providerKeeper.SetChannelToChain(ctx, "channel-1", "chain-1") - providerKeeper.SetChannelToChain(ctx, "channel-2", "chain-2") - - // Execute on recv for chain-1, confirm v1 result ack is returned - err := executeOnRecvVSCMaturedPacket(t, &providerKeeper, ctx, "channel-1", 1) - require.NoError(t, err) - - // Now queue a slash packet data instance for chain-2, confirm v1 result ack is returned - err = executeOnRecvVSCMaturedPacket(t, &providerKeeper, ctx, "channel-2", 2) - require.NoError(t, err) -} - // TestOnRecvDowntimeSlashPacket tests the OnRecvSlashPacket method specifically for downtime slash packets. func TestOnRecvDowntimeSlashPacket(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) @@ -173,7 +150,7 @@ func TestOnRecvDowntimeSlashPacket(t *testing.T) { providerKeeper.SetValsetUpdateBlockHeight(ctx, packetData.ValsetUpdateId, uint64(15)) // Set consumer validator - providerKeeper.SetConsumerValidator(ctx, "chain-1", providertypes.ConsumerValidator{ + providerKeeper.SetConsumerValidator(ctx, "chain-1", providertypes.ConsensusValidator{ ProviderConsAddr: packetData.Validator.Address, }) @@ -184,7 +161,7 @@ func TestOnRecvDowntimeSlashPacket(t *testing.T) { require.NoError(t, err) // Set consumer validator - providerKeeper.SetConsumerValidator(ctx, "chain-2", providertypes.ConsumerValidator{ + providerKeeper.SetConsumerValidator(ctx, "chain-2", providertypes.ConsensusValidator{ ProviderConsAddr: packetData.Validator.Address, }) @@ -197,7 +174,7 @@ func TestOnRecvDowntimeSlashPacket(t *testing.T) { providerKeeper.SetSlashMeter(ctx, math.NewInt(5)) // Set the consumer validator - providerKeeper.SetConsumerValidator(ctx, "chain-1", providertypes.ConsumerValidator{ProviderConsAddr: packetData.Validator.Address}) + providerKeeper.SetConsumerValidator(ctx, "chain-1", providertypes.ConsensusValidator{ProviderConsAddr: packetData.Validator.Address}) // Mock call to GetEffectiveValPower, so that it returns 2. providerAddr := providertypes.NewProviderConsAddress(packetData.Validator.Address) @@ -258,22 +235,6 @@ func TestOnRecvDoubleSignSlashPacket(t *testing.T) { require.False(t, providerKeeper.GetSlashLog(ctx, randomAddress)) } -func executeOnRecvVSCMaturedPacket(t *testing.T, providerKeeper *keeper.Keeper, ctx sdk.Context, - channelID string, ibcSeqNum uint64, -) error { - t.Helper() - // Instantiate vsc matured packet data and bytes - data := testkeeper.GetNewVSCMaturedPacketData() - dataBz, err := data.Marshal() - require.NoError(t, err) - - return providerKeeper.OnRecvVSCMaturedPacket( - ctx, - channeltypes.NewPacket(dataBz, ibcSeqNum, "srcPort", "srcChan", "provider-port", channelID, clienttypes.Height{}, 1), - data, - ) -} - func executeOnRecvSlashPacket(t *testing.T, providerKeeper *keeper.Keeper, ctx sdk.Context, channelID string, ibcSeqNum uint64, packetData ccv.SlashPacketData, ) (ccv.PacketAckResult, error) { @@ -491,7 +452,7 @@ func TestHandleSlashPacket(t *testing.T) { // Setup consumer address to provider address mapping. require.NotEmpty(t, tc.packetData.Validator.Address) providerKeeper.SetValidatorByConsumerAddr(ctx, chainId, consumerConsAddr, providerConsAddr) - providerKeeper.SetConsumerValidator(ctx, chainId, providertypes.ConsumerValidator{ProviderConsAddr: providerConsAddr.Address.Bytes()}) + providerKeeper.SetConsumerValidator(ctx, chainId, providertypes.ConsensusValidator{ProviderConsAddr: providerConsAddr.Address.Bytes()}) // Execute method and assert expected mock calls. providerKeeper.HandleSlashPacket(ctx, chainId, tc.packetData) @@ -510,184 +471,6 @@ func TestHandleSlashPacket(t *testing.T) { } } -// TestHandleVSCMaturedPacket tests the handling of VSCMatured packets. -// Note that this method also tests the behaviour of AfterUnbondingInitiated. -func TestHandleVSCMaturedPacket(t *testing.T) { - pk, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - // Init vscID - pk.SetValidatorSetUpdateId(ctx, 1) - - // Start first unbonding without any consumers registered - var unbondingOpId uint64 = 1 - gomock.InOrder( - mocks.MockStakingKeeper.EXPECT().GetUnbondingType(ctx, unbondingOpId).Return(stakingtypes.UnbondingType_Undefined, stakingtypes.ErrNoUnbondingType), - ) - - err := pk.Hooks().AfterUnbondingInitiated(ctx, unbondingOpId) - require.NoError(t, err) - // Check that no unbonding op was stored - _, found := pk.GetUnbondingOp(ctx, unbondingOpId) - require.False(t, found) - - // Increment vscID - pk.IncrementValidatorSetUpdateId(ctx) - require.Equal(t, uint64(2), pk.GetValidatorSetUpdateId(ctx)) - - // 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", providertypes.ConsumerValidator{ProviderConsAddr: valsPk[0].Address()}) - - // Start second unbonding - unbondingOpId = 2 - gomock.InOrder( - mocks.MockStakingKeeper.EXPECT().GetUnbondingType(ctx, unbondingOpId).Return(stakingtypes.UnbondingType_UnbondingDelegation, nil), - mocks.MockStakingKeeper.EXPECT().GetUnbondingDelegationByUnbondingID(ctx, unbondingOpId).Return( - stakingtypes.UnbondingDelegation{ - ValidatorAddress: sdk.ValAddress([]byte{1}).String(), - }, nil), - mocks.MockStakingKeeper.EXPECT().GetValidator(ctx, sdk.ValAddress([]byte{1})). - Return(vals[0], nil), - 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 - expectedChains := []string{"chain-1"} - unbondingOp, found := pk.GetUnbondingOp(ctx, unbondingOpId) - require.True(t, found) - require.Equal(t, unbondingOpId, unbondingOp.Id) - require.Equal(t, expectedChains, unbondingOp.UnbondingConsumerChains) - // Check that the unbonding op index was stored - expectedUnbondingOpIds := []uint64{unbondingOpId} - ids, found := pk.GetUnbondingOpIndex(ctx, "chain-1", pk.GetValidatorSetUpdateId(ctx)) - require.True(t, found) - require.Equal(t, expectedUnbondingOpIds, ids) - - // Increment vscID - pk.IncrementValidatorSetUpdateId(ctx) - require.Equal(t, uint64(3), pk.GetValidatorSetUpdateId(ctx)) - - // Registered second consumer - pk.SetConsumerClientId(ctx, "chain-2", "client-2") - - // Opt-in both validators to second consumer - pk.SetConsumerValidator(ctx, "chain-2", providertypes.ConsumerValidator{ProviderConsAddr: valsPk[0].Address()}) - pk.SetConsumerValidator(ctx, "chain-2", providertypes.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, nil), - mocks.MockStakingKeeper.EXPECT().GetRedelegationByUnbondingID(ctx, id).Return( - stakingtypes.Redelegation{ - ValidatorSrcAddress: sdk.ValAddress([]byte{1}).String(), - }, nil), - mocks.MockStakingKeeper.EXPECT().GetValidator(ctx, sdk.ValAddress([]byte{1})). - Return(vals[0], nil), - mocks.MockStakingKeeper.EXPECT().PutUnbondingOnHold(ctx, id).Return(nil), - ) - err = pk.Hooks().AfterUnbondingInitiated(ctx, id) - require.NoError(t, err) - } - // Check that the unbonding ops were stored - expectedChains = []string{"chain-1", "chain-2"} - for _, id := range unbondingOpIds { - unbondingOp, found = pk.GetUnbondingOp(ctx, id) - require.True(t, found) - require.Equal(t, id, unbondingOp.Id) - require.Equal(t, expectedChains, unbondingOp.UnbondingConsumerChains) - } - // Check that the unbonding op index was stored - for _, chainID := range expectedChains { - ids, found := pk.GetUnbondingOpIndex(ctx, chainID, pk.GetValidatorSetUpdateId(ctx)) - require.True(t, found) - 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, nil), - mocks.MockStakingKeeper.EXPECT().GetValidatorByUnbondingID(ctx, unbondingOpId).Return( - stakingtypes.Validator{ - OperatorAddress: sdk.ValAddress([]byte{1}).String(), - }, nil), - mocks.MockStakingKeeper.EXPECT().GetValidator(ctx, sdk.ValAddress([]byte{1})). - Return(vals[1], nil), - 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 - pk.HandleVSCMaturedPacket(ctx, "chain-1", ccv.VSCMaturedPacketData{ValsetUpdateId: 1}) - require.Empty(t, pk.ConsumeMaturedUnbondingOps(ctx)) - - // Handle VSCMatured packet from chain-1 for vscID 2. - pk.HandleVSCMaturedPacket(ctx, "chain-1", ccv.VSCMaturedPacketData{ValsetUpdateId: 2}) - // Check that the unbonding operation with ID=2 can complete - require.Equal(t, []uint64{2}, pk.ConsumeMaturedUnbondingOps(ctx)) - // Check that the unbonding op index was removed - _, found = pk.GetUnbondingOpIndex(ctx, "chain-1", 2) - require.False(t, found) - - // Handle VSCMatured packet from chain-2 for vscID 3. - pk.HandleVSCMaturedPacket(ctx, "chain-2", ccv.VSCMaturedPacketData{ValsetUpdateId: 3}) - // Check that the unbonding operations with IDs 3 and 4 no longer wait for chain-2 - expectedChains = []string{"chain-1"} - unbondingOpIds = []uint64{3, 4} - for _, id := range unbondingOpIds { - unbondingOp, found := pk.GetUnbondingOp(ctx, id) - require.True(t, found) - require.Equal(t, id, unbondingOp.Id) - require.Equal(t, expectedChains, unbondingOp.UnbondingConsumerChains) - } - // Check that no unbonding operation can complete - require.Empty(t, pk.ConsumeMaturedUnbondingOps(ctx)) - // Check that the unbonding op index was removed - _, found = pk.GetUnbondingOpIndex(ctx, "chain-2", 3) - require.False(t, found) - - // Handle VSCMatured packet from chain-1 for vscID 3. - pk.HandleVSCMaturedPacket(ctx, "chain-1", ccv.VSCMaturedPacketData{ValsetUpdateId: 3}) - // Check that the unbonding operations with IDs 3 and 4 can complete - require.Equal(t, unbondingOpIds, pk.ConsumeMaturedUnbondingOps(ctx)) - // Check that the unbonding op index was removed - _, found = pk.GetUnbondingOpIndex(ctx, "chain-1", 3) - require.False(t, found) -} - // TestSendVSCPacketsToChainFailure tests the SendVSCPacketsToChain method failing func TestSendVSCPacketsToChainFailure(t *testing.T) { // Keeper setup @@ -812,7 +595,7 @@ func TestEndBlockVSU(t *testing.T) { var lastValidators []stakingtypes.Validator var powers []int64 for i := 0; i < 4; i++ { - validator := crypto.NewCryptoIdentityFromIntSeed(i).SDKStakingValidator() + validator := cryptotestutil.NewCryptoIdentityFromIntSeed(i).SDKStakingValidator() lastValidators = append(lastValidators, validator) valAdrr, err := sdk.ValAddressFromBech32(validator.GetOperator()) require.NoError(t, err) @@ -820,7 +603,13 @@ func TestEndBlockVSU(t *testing.T) { powers = append(powers, int64(i+1)) } - testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 5, lastValidators, powers, -1) + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 5, lastValidators, -1) + + sort.Slice(lastValidators, func(i, j int) bool { + return lastValidators[i].GetConsensusPower(sdk.DefaultPowerReduction) > + lastValidators[j].GetConsensusPower(sdk.DefaultPowerReduction) + }) + mocks.MockStakingKeeper.EXPECT().GetBondedValidatorsByPower(gomock.Any()).Return(lastValidators, nil).AnyTimes() // set a sample client for a consumer chain so that `GetAllConsumerChains` in `QueueVSCPackets` iterates at least once providerKeeper.SetConsumerClientId(ctx, chainID, "clientID") @@ -848,6 +637,74 @@ func TestEndBlockVSU(t *testing.T) { require.Equal(t, 1, len(providerKeeper.GetPendingVSCPackets(ctx, chainID))) } +// TestProviderValidatorUpdates tests that the provider validator updates are correctly calculated, +// taking into account the MaxProviderConsensusValidators parameter +func TestProviderValidatorUpdates(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // Mocking bonded validators in the staking keeper. + // be aware that the powers need to be in descending order + validators := []stakingtypes.Validator{ + createStakingValidator(ctx, mocks, 3, 30, 3), + createStakingValidator(ctx, mocks, 2, 20, 2), + createStakingValidator(ctx, mocks, 1, 10, 1), + } + mocks.MockStakingKeeper.EXPECT().GetBondedValidatorsByPower(ctx).Return(validators, nil).Times(1) + + // set up a validator that we will only use for the last provider consensus validator set + removedValidator := createStakingValidator(ctx, mocks, 4, 40, 4) + + // Set up the last provider consensus validators + consensusVals := make([]providertypes.ConsensusValidator, 0, len(validators)) + // add the removed validator + removedConsensusVal, err := providerKeeper.CreateProviderConsensusValidator(ctx, removedValidator) + require.NoError(t, err) + consensusVals = append(consensusVals, removedConsensusVal) + for _, val := range validators[1:] { // skip the first validator (validator 3) + consensusVal, err := providerKeeper.CreateProviderConsensusValidator(ctx, val) + require.NoError(t, err) + consensusVals = append(consensusVals, consensusVal) + } + // consensusVals is now [removedValidator, validator 2, validator 1] + + // Set the last provider consensus validator set + providerKeeper.SetLastProviderConsensusValSet(ctx, consensusVals) + + // Set the max number of validators + maxProviderConsensusValidators := int64(2) + params := providerKeeper.GetParams(ctx) + params.MaxProviderConsensusValidators = maxProviderConsensusValidators + providerKeeper.SetParams(ctx, params) + + // expected validator updates + + // validator 3 is added + // removed validator is set to 0 power + // validator 1 is set to 0 power (because maxProviderConsensusValidators is 2) + // validator 2 is untouched + expectedUpdates := []abci.ValidatorUpdate{ + { + PubKey: testkeeper.Must(validators[0].CmtConsPublicKey()), + Power: 30, + }, + { + PubKey: testkeeper.Must(removedValidator.CmtConsPublicKey()), + Power: 0, + }, + { + PubKey: testkeeper.Must(validators[2].CmtConsPublicKey()), + Power: 0, + }, + } + + // Execute the function + updates := providerKeeper.ProviderValidatorUpdates(ctx) + + // Assertions + require.ElementsMatch(t, expectedUpdates, updates, "The validator updates should match the expected updates") +} + // TestQueueVSCPacketsWithPowerCapping tests queueing validator set updates with power capping func TestQueueVSCPacketsWithPowerCapping(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) @@ -875,7 +732,7 @@ func TestQueueVSCPacketsWithPowerCapping(t *testing.T) { valEPubKey, _ := valE.TmConsPublicKey() mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valEConsAddr).Return(valE, nil).AnyTimes() - testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 5, []stakingtypes.Validator{valA, valB, valC, valD, valE}, []int64{1, 3, 4, 8, 16}, -1) + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 5, []stakingtypes.Validator{valA, valB, valC, valD, valE}, -1) // add a consumer chain providerKeeper.SetConsumerClientId(ctx, "chainID", "clientID") @@ -894,6 +751,11 @@ func TestQueueVSCPacketsWithPowerCapping(t *testing.T) { // set a power-capping of 40% providerKeeper.SetValidatorsPowerCap(ctx, "chainID", 40) + // set max provider consensus vals to include all validators + params := providerKeeper.GetParams(ctx) + params.MaxProviderConsensusValidators = 180 + providerKeeper.SetParams(ctx, params) + providerKeeper.QueueVSCPackets(ctx) actualQueuedVSCPackets := providerKeeper.GetPendingVSCPackets(ctx, "chainID") @@ -925,3 +787,39 @@ func TestQueueVSCPacketsWithPowerCapping(t *testing.T) { require.Equal(t, expectedQueuedVSCPackets, actualQueuedVSCPackets) } + +// TestBlocksUntilNextEpoch tests the `BlocksUntilNextEpoch` method +func TestBlocksUntilNextEpoch(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // 10 blocks constitute an epoch + params := providertypes.DefaultParams() + params.BlocksPerEpoch = 10 + providerKeeper.SetParams(ctx, params) + + // with block height of 1 we expect 9 blocks until the next epoch + ctx = ctx.WithBlockHeight(1) + require.Equal(t, int64(9), providerKeeper.BlocksUntilNextEpoch(ctx)) + + // with block height of 5 we expect 5 blocks until the next epoch + ctx = ctx.WithBlockHeight(5) + require.Equal(t, int64(5), providerKeeper.BlocksUntilNextEpoch(ctx)) + + // with block height of 10 we expect 0 blocks until the next epoch, since + // this epoch is the one where we send the VSC packet + ctx = ctx.WithBlockHeight(10) + require.Equal(t, int64(0), providerKeeper.BlocksUntilNextEpoch(ctx)) + + // with block height of 11 we expect 9 blocks until the next epoch + ctx = ctx.WithBlockHeight(11) + require.Equal(t, int64(9), providerKeeper.BlocksUntilNextEpoch(ctx)) + + // with block height of 15 we expect 5 blocks until the next epoch + ctx = ctx.WithBlockHeight(15) + require.Equal(t, int64(5), providerKeeper.BlocksUntilNextEpoch(ctx)) + + // with block height of 19 we expect 1 block until the next epoch + ctx = ctx.WithBlockHeight(19) + require.Equal(t, int64(1), providerKeeper.BlocksUntilNextEpoch(ctx)) +} diff --git a/x/ccv/provider/keeper/staking_keeper_interface.go b/x/ccv/provider/keeper/staking_keeper_interface.go new file mode 100644 index 0000000000..a0b6e364c7 --- /dev/null +++ b/x/ccv/provider/keeper/staking_keeper_interface.go @@ -0,0 +1,80 @@ +package keeper + +import ( + "context" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// IterateBondedValidatorsByPower iterates over the consensus-active validators by power. +// The same as IterateBondedValidatorsByPower in the StakingKeeper, +// but only returns the first MaxProviderConsensusValidators validators. +// This is used to implement the interface of the staking keeper to interact with +// modules that need to reference the consensus-active validators. +func (k Keeper) IterateBondedValidatorsByPower(ctx context.Context, fn func(index int64, validator stakingtypes.ValidatorI) (stop bool)) error { + maxProviderConsensusVals := k.GetMaxProviderConsensusValidators(sdk.UnwrapSDKContext(ctx)) + counter := int64(0) + return k.stakingKeeper.IterateBondedValidatorsByPower(ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) { + if counter >= maxProviderConsensusVals { + return true + } + counter++ + return fn(index, validator) + }) +} + +// TotalBondedTokens gets the amount of tokens of the consensus-active validators. +// The same as TotalBondedTokens in the StakingKeeper, but only counts bonded tokens +// of the first MaxProviderConsensusValidators bonded validators. +// This is used to implement the interface of the staking keeper to interface with +// modules that need to reference the consensus-active validators. +func (k Keeper) TotalBondedTokens(ctx context.Context) (math.Int, error) { + // iterate through the bonded validators + totalBondedTokens := math.ZeroInt() + + k.IterateBondedValidatorsByPower(ctx, func(_ int64, validator stakingtypes.ValidatorI) (stop bool) { + tokens := validator.GetBondedTokens() + totalBondedTokens = totalBondedTokens.Add(tokens) + return false + }) + + return totalBondedTokens, nil +} + +// IterateDelegations is the same as IterateDelegations in the StakingKeeper. +// Necessary to implement the interface of the staking keeper to interface with +// other modules. +func (k Keeper) IterateDelegations(ctx context.Context, delegator sdk.AccAddress, fn func(index int64, delegation stakingtypes.DelegationI) (stop bool)) error { + return k.stakingKeeper.IterateDelegations(ctx, delegator, fn) +} + +// StakingTokenSupply is the same as StakingTotalSupply in the StakingKeeper. +// Necessary to implement the interface of the staking keeper to interface with +// other modules. +func (k Keeper) StakingTokenSupply(ctx context.Context) (math.Int, error) { + return k.stakingKeeper.StakingTokenSupply(ctx) +} + +// BondedRatio gets the ratio of tokens staked to validators active in the consensus +// to the total supply of tokens. +// Same as BondedRatio in the StakingKeeper, but only counts +// tokens of the first MaxProviderConsensusValidators bonded validators. +func (k Keeper) BondedRatio(ctx context.Context) (math.LegacyDec, error) { + totalSupply, err := k.StakingTokenSupply(ctx) + if err != nil { + return math.LegacyZeroDec(), err + } + + bondedTokens, err := k.TotalBondedTokens(ctx) + if err != nil { + return math.LegacyZeroDec(), err + } + + if !totalSupply.IsPositive() { + return math.LegacyZeroDec(), nil + } + + return math.LegacyNewDecFromInt(bondedTokens).QuoInt(totalSupply), nil +} diff --git a/x/ccv/provider/keeper/staking_keeper_interface_test.go b/x/ccv/provider/keeper/staking_keeper_interface_test.go new file mode 100644 index 0000000000..2eb9c9f1bd --- /dev/null +++ b/x/ccv/provider/keeper/staking_keeper_interface_test.go @@ -0,0 +1,131 @@ +package keeper_test + +import ( + "sort" + "testing" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +// TestStakingKeeperInterface tests +// * IterateBondedValidatorsByPower +// * TotalBondedTokens +// * BondedRatio +func TestStakingKeeperInterface(t *testing.T) { + tests := []struct { + name string + maxValidators int64 + powers []int64 + expectedPowers []int64 + mockTotalSupply int64 + expectedTotalBonded math.Int + expectedBondedRatio math.LegacyDec + }{ + { + name: "no validators", + maxValidators: 5, + powers: []int64{}, + expectedPowers: []int64{}, + mockTotalSupply: 100, + expectedTotalBonded: math.ZeroInt(), + expectedBondedRatio: math.LegacyZeroDec(), + }, + { + name: "validators less than max", + maxValidators: 5, + powers: []int64{10, 20, 30}, + expectedPowers: []int64{10, 20, 30}, // get all validators back + mockTotalSupply: 100, + expectedTotalBonded: math.NewInt(60), + expectedBondedRatio: math.LegacyNewDec(60).Quo(math.LegacyNewDec(100)), // 60% bonded + }, + { + name: "validators more than max", + maxValidators: 2, + powers: []int64{10, 20, 30}, + expectedPowers: []int64{30, 20}, // get the top 2 validators back + mockTotalSupply: 100, + // 30 + 20 = 50 + expectedTotalBonded: math.NewInt(50), + expectedBondedRatio: math.LegacyNewDec(50).Quo(math.LegacyNewDec(100)), // 50% bonded + }, + { + name: "validators equal to max", + maxValidators: 3, + powers: []int64{10, 20, 30}, + expectedPowers: []int64{30, 20, 10}, // get all validators back + mockTotalSupply: 60, + // 30 + 20 + 10 = 60 + expectedTotalBonded: math.NewInt(60), + expectedBondedRatio: math.LegacyNewDec(60).Quo(math.LegacyNewDec(60)), // 100% bonded + }, + { + name: "validators with equal powers", + maxValidators: 3, + powers: []int64{10, 10, 10, 20, 20, 30, 30}, + expectedPowers: []int64{30, 30, 20}, // get the top 3 validators back + mockTotalSupply: 1000, + // 30 + 30 + 20 = 80 + expectedTotalBonded: math.NewInt(80), + expectedBondedRatio: math.LegacyNewDec(80).Quo(math.LegacyNewDec(1000)), // 8% bonded + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + counter := int64(0) + vals, _ := createStakingValidatorsAndMocks(ctx, mocks, tt.powers...) + + params := providerKeeper.GetParams(ctx) + params.MaxProviderConsensusValidators = tt.maxValidators + providerKeeper.SetParams(ctx, params) + + // sort vals by descending number of tokens + sort.Slice( + vals, + func(i, j int) bool { + return vals[i].GetBondedTokens().Int64() > vals[j].GetTokens().Int64() + }, + ) + + mocks.MockStakingKeeper.EXPECT().IterateBondedValidatorsByPower(gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx sdk.Context, cb func(int64, stakingtypes.ValidatorI) bool) error { + for i, val := range vals { + if stop := cb(int64(i), val); stop { + break + } + } + return nil + }).AnyTimes() + actualValPowers := []int64{} + err := providerKeeper.IterateBondedValidatorsByPower(ctx, func(index int64, validator types.ValidatorI) (stop bool) { + counter++ + actualValPowers = append(actualValPowers, validator.GetTokens().Int64()) + return false + }) + require.NoError(t, err) + // we don't check that the elements exactly match; just matching the powers is good enough + require.ElementsMatch(t, tt.expectedPowers, actualValPowers) + + // check bonded ratio and total bonded + mocks.MockStakingKeeper.EXPECT().StakingTokenSupply(gomock.Any()).Return(math.NewInt(tt.mockTotalSupply), nil).AnyTimes() + + totalBonded, err := providerKeeper.TotalBondedTokens(ctx) + require.NoError(t, err) + require.Equal(t, tt.expectedTotalBonded, totalBonded) + + bondedRatio, err := providerKeeper.BondedRatio(ctx) + require.NoError(t, err) + require.Equal(t, tt.expectedBondedRatio, bondedRatio) + }) + } +} diff --git a/x/ccv/provider/keeper/throttle_legacy.go b/x/ccv/provider/keeper/throttle_legacy.go deleted file mode 100644 index 53c6f39c72..0000000000 --- a/x/ccv/provider/keeper/throttle_legacy.go +++ /dev/null @@ -1,111 +0,0 @@ -package keeper - -import ( - "fmt" - - storetypes "cosmossdk.io/store/types" - 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" -) - -// Pending packet data type enum, used to encode the type of packet data stored at each entry in the mutual queue. -// Note this type is copy/pasted from throttle v1 code. -const ( - slashPacketData byte = iota - vscMaturedPacketData -) - -// Deprecated: LegacyGetAllThrottledPacketData is deprecated for ICS >= v4.0.0. -// LegacyGetAllThrottledPacketData returns all throttled packet data that was queued on the provider for a given consumer chain. -func (k Keeper) LegacyGetAllThrottledPacketData(ctx sdktypes.Context, consumerChainID string) ( - slashData []ccvtypes.SlashPacketData, vscMaturedData []ccvtypes.VSCMaturedPacketData, -) { - slashData = []ccvtypes.SlashPacketData{} - vscMaturedData = []ccvtypes.VSCMaturedPacketData{} - - store := ctx.KVStore(k.storeKey) - iteratorPrefix := providertypes.ChainIdWithLenKey(providertypes.ThrottledPacketDataBytePrefix, consumerChainID) - iterator := storetypes.KVStorePrefixIterator(store, iteratorPrefix) - defer iterator.Close() - - for ; iterator.Valid(); iterator.Next() { - bz := iterator.Value() - switch bz[0] { - case slashPacketData: - d := ccvtypes.SlashPacketData{} - if err := d.Unmarshal(bz[1:]); err != nil { - k.Logger(ctx).Error(fmt.Sprintf("failed to unmarshal slash packet data: %v", err)) - continue - } - slashData = append(slashData, d) - case vscMaturedPacketData: - d := ccvtypes.VSCMaturedPacketData{} - if err := d.Unmarshal(bz[1:]); err != nil { - k.Logger(ctx).Error(fmt.Sprintf("failed to unmarshal vsc matured packet data: %v", err)) - continue - } - vscMaturedData = append(vscMaturedData, d) - default: - k.Logger(ctx).Error(fmt.Sprintf("invalid packet data type: %v", bz[0])) - continue - } - } - - return slashData, vscMaturedData -} - -// Deprecated: LegacyDeleteThrottledPacketDataForConsumer is deprecated for ICS >= v4.0.0. -// LegacyDeleteThrottledPacketDataForConsumer removes all throttled packet data that was queued on the provider for a given consumer chain. -func (k Keeper) LegacyDeleteThrottledPacketDataForConsumer(ctx sdktypes.Context, consumerChainID string) { - store := ctx.KVStore(k.storeKey) - iteratorPrefix := providertypes.ChainIdWithLenKey(providertypes.ThrottledPacketDataBytePrefix, consumerChainID) - iterator := storetypes.KVStorePrefixIterator(store, iteratorPrefix) - defer iterator.Close() - - keysToDel := [][]byte{} - for ; iterator.Valid(); iterator.Next() { - keysToDel = append(keysToDel, iterator.Key()) - } - // Delete data for this consumer - for _, key := range keysToDel { - store.Delete(key) - } - - // Delete size of data queue for this consumer - store.Delete(providertypes.ThrottledPacketDataSizeKey(consumerChainID)) -} - -// Deprecated: LegacyQueueThrottledPacketData is deprecated for ICS >= v4.0.0. -// LegacyQueueThrottledPacketData queues throttled packet data for a given consumer chain on the provider. -// The method should not be used because the provider does not process throttled packet data anymore. -func (k Keeper) LegacyQueueThrottledPacketData( - ctx sdktypes.Context, consumerChainID string, ibcSeqNum uint64, packetData interface{}, -) error { - store := ctx.KVStore(k.storeKey) - - var bz []byte - var err error - switch data := packetData.(type) { - case ccvtypes.SlashPacketData: - bz, err = data.Marshal() - if err != nil { - return fmt.Errorf("failed to marshal slash packet data: %v", err) - } - bz = append([]byte{slashPacketData}, bz...) - case ccvtypes.VSCMaturedPacketData: - bz, err = data.Marshal() - if err != nil { - return fmt.Errorf("failed to marshal vsc matured packet data: %v", err) - } - bz = append([]byte{vscMaturedPacketData}, bz...) - default: - // Indicates a developer error, this method should only be called - // by tests, QueueThrottledSlashPacketData, or QueueThrottledVSCMaturedPacketData. - panic(fmt.Sprintf("unexpected packet data type: %T", data)) - } - - store.Set(providertypes.ThrottledPacketDataKey(consumerChainID, ibcSeqNum), bz) - return nil -} diff --git a/x/ccv/provider/keeper/validator_set_storage.go b/x/ccv/provider/keeper/validator_set_storage.go new file mode 100644 index 0000000000..cad322ed6b --- /dev/null +++ b/x/ccv/provider/keeper/validator_set_storage.go @@ -0,0 +1,121 @@ +package keeper + +import ( + "fmt" + + "cosmossdk.io/math" + storetypes "cosmossdk.io/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" +) + +// The functions here are meant to provide a generic interface to a validator set that +// is stored under a certain prefix. +// It is used to store the validator set for the consumer chain and the last validator set +// sent to the consensus engine on the provider; see +// provider_consensus.go and validator_set_update.go + +// GetValidatorKey constructs the key to access a given validator, stored under a given prefix. +// This method is public for testing. +func GetValidatorKey(prefix []byte, providerConsAddr types.ProviderConsAddress) []byte { + return append(prefix, providerConsAddr.ToSdkConsAddr()...) +} + +// setValidator stores the given `validator` in the validator set stored under the given prefix. +func (k Keeper) setValidator( + ctx sdk.Context, + prefix []byte, + validator types.ConsensusValidator, +) error { + store := ctx.KVStore(k.storeKey) + bz, err := validator.Marshal() + if err != nil { + return fmt.Errorf("marshalling ConsensusValidator: %w", err) + } + + store.Set(GetValidatorKey(prefix, types.NewProviderConsAddress(validator.ProviderConsAddr)), bz) + return nil +} + +// setValSet resets the validator set stored under the given prefix to the provided `nextValidators`. +func (k Keeper) setValSet(ctx sdk.Context, prefix []byte, nextValidators []types.ConsensusValidator) error { + k.deleteValSet(ctx, prefix) + for _, val := range nextValidators { + err := k.setValidator(ctx, prefix, val) + if err != nil { + return err + } + } + return nil +} + +// deleteValidator removes validator with `providerConsAddr` address from the +// validator set stored under the given prefix. +func (k Keeper) deleteValidator( + ctx sdk.Context, + prefix []byte, + providerConsAddr types.ProviderConsAddress, +) { + store := ctx.KVStore(k.storeKey) + store.Delete(GetValidatorKey(prefix, providerConsAddr)) +} + +// deleteValSet deletes all the stored consumer validators under the given prefix. +func (k Keeper) deleteValSet( + ctx sdk.Context, + prefix []byte, +) { + store := ctx.KVStore(k.storeKey) + iterator := storetypes.KVStorePrefixIterator(store, prefix) + + var keysToDel [][]byte + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + keysToDel = append(keysToDel, iterator.Key()) + } + for _, delKey := range keysToDel { + store.Delete(delKey) + } +} + +// isValidator returns `true` if the validator with `providerConsAddr` exists +// in the validator set stored under the given prefix. +func (k Keeper) isValidator(ctx sdk.Context, prefix []byte, providerConsAddr types.ProviderConsAddress) bool { + store := ctx.KVStore(k.storeKey) + return store.Get(GetValidatorKey(prefix, providerConsAddr)) != nil +} + +// getValSet returns all the validators stored under the given prefix. +func (k Keeper) getValSet( + ctx sdk.Context, + prefix []byte, +) (validators []types.ConsensusValidator, err error) { + store := ctx.KVStore(k.storeKey) + iterator := storetypes.KVStorePrefixIterator(store, prefix) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + iterator.Value() + var validator types.ConsensusValidator + if err := validator.Unmarshal(iterator.Value()); err != nil { + return validators, fmt.Errorf("unmarshalling ConsensusValidator: %w", err) + } + validators = append(validators, validator) + } + + return validators, err +} + +// getTotalPower computes the total power of all the consumer validators stored under the given prefix. +func (k Keeper) getTotalPower(ctx sdk.Context, prefix []byte) (math.Int, error) { + totalPower := math.ZeroInt() + validators, err := k.getValSet(ctx, prefix) + if err != nil { + panic(fmt.Errorf("retrieving validator set: %w", err)) + } + for _, val := range validators { + totalPower = totalPower.Add(math.NewInt(val.Power)) + } + return totalPower, nil +} diff --git a/x/ccv/provider/keeper/validator_set_update.go b/x/ccv/provider/keeper/validator_set_update.go index d7f764171e..1c55f26553 100644 --- a/x/ccv/provider/keeper/validator_set_update.go +++ b/x/ccv/provider/keeper/validator_set_update.go @@ -3,7 +3,6 @@ package keeper import ( "fmt" - storetypes "cosmossdk.io/store/types" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -13,28 +12,24 @@ import ( ccv "github.com/cosmos/interchain-security/v5/x/ccv/types" ) +// GetConsumerChainConsensusValidatorsKey returns the store key for consumer validators of the consumer chain with `chainID` +func (k Keeper) GetConsumerChainConsensusValidatorsKey(ctx sdk.Context, chainID string) []byte { + return types.ChainIdWithLenKey(types.ConsumerValidatorKeyPrefix(), chainID) +} + // SetConsumerValidator sets provided consumer `validator` on the consumer chain with `chainID` func (k Keeper) SetConsumerValidator( ctx sdk.Context, chainID string, - validator types.ConsumerValidator, + validator types.ConsensusValidator, ) { - store := ctx.KVStore(k.storeKey) - bz, err := validator.Marshal() - if err != nil { - panic(fmt.Errorf("failed to marshal ConsumerValidator: %w", err)) - } - - store.Set(types.ConsumerValidatorKey(chainID, validator.ProviderConsAddr), bz) + k.setValidator(ctx, k.GetConsumerChainConsensusValidatorsKey(ctx, chainID), validator) } // 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) - } +func (k Keeper) SetConsumerValSet(ctx sdk.Context, chainID string, nextValidators []types.ConsensusValidator) { + k.setValSet(ctx, k.GetConsumerChainConsensusValidatorsKey(ctx, chainID), nextValidators) } // DeleteConsumerValidator removes consumer validator with `providerAddr` address @@ -43,8 +38,7 @@ func (k Keeper) DeleteConsumerValidator( chainID string, providerConsAddr types.ProviderConsAddress, ) { - store := ctx.KVStore(k.storeKey) - store.Delete(types.ConsumerValidatorKey(chainID, providerConsAddr.ToSdkConsAddr())) + k.deleteValidator(ctx, k.GetConsumerChainConsensusValidatorsKey(ctx, chainID), providerConsAddr) } // DeleteConsumerValSet deletes all the stored consumer validators for chain `chainID` @@ -52,37 +46,25 @@ func (k Keeper) DeleteConsumerValSet( ctx sdk.Context, chainID string, ) { - store := ctx.KVStore(k.storeKey) - key := types.ChainIdWithLenKey(types.ConsumerValidatorBytePrefix, chainID) - iterator := storetypes.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) - } + k.deleteValSet(ctx, k.GetConsumerChainConsensusValidatorsKey(ctx, chainID)) } // 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 { - store := ctx.KVStore(k.storeKey) - return store.Get(types.ConsumerValidatorKey(chainID, providerAddr.ToSdkConsAddr())) != nil + return k.isValidator(ctx, k.GetConsumerChainConsensusValidatorsKey(ctx, chainID), providerAddr) } // 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) { +func (k Keeper) GetConsumerValidator(ctx sdk.Context, chainID string, providerAddr types.ProviderConsAddress) (types.ConsensusValidator, bool) { store := ctx.KVStore(k.storeKey) marshalledConsumerValidator := store.Get(types.ConsumerValidatorKey(chainID, providerAddr.ToSdkConsAddr())) if marshalledConsumerValidator == nil { - return types.ConsumerValidator{}, false + return types.ConsensusValidator{}, false } - var validator types.ConsumerValidator + var validator types.ConsensusValidator if err := validator.Unmarshal(marshalledConsumerValidator); err != nil { panic(fmt.Errorf("failed to unmarshal ConsumerValidator: %w", err)) } @@ -94,59 +76,45 @@ func (k Keeper) GetConsumerValidator(ctx sdk.Context, chainID string, providerAd func (k Keeper) GetConsumerValSet( ctx sdk.Context, chainID string, -) (validators []types.ConsumerValidator) { - store := ctx.KVStore(k.storeKey) - key := types.ChainIdWithLenKey(types.ConsumerValidatorBytePrefix, chainID) - iterator := storetypes.KVStorePrefixIterator(store, key) - defer iterator.Close() - - for ; iterator.Valid(); iterator.Next() { - iterator.Value() - var validator types.ConsumerValidator - if err := validator.Unmarshal(iterator.Value()); err != nil { - panic(fmt.Errorf("failed to unmarshal ConsumerValidator: %w", err)) - } - validators = append(validators, validator) - } - - return validators +) ([]types.ConsensusValidator, error) { + return k.getValSet(ctx, k.GetConsumerChainConsensusValidatorsKey(ctx, chainID)) } // 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( - currentValidators []types.ConsumerValidator, - nextValidators []types.ConsumerValidator, + currentValidators []types.ConsensusValidator, + nextValidators []types.ConsensusValidator, ) []abci.ValidatorUpdate { var updates []abci.ValidatorUpdate - isCurrentValidator := make(map[string]types.ConsumerValidator, len(currentValidators)) + isCurrentValidator := make(map[string]types.ConsensusValidator, len(currentValidators)) for _, val := range currentValidators { - isCurrentValidator[val.ConsumerPublicKey.String()] = val + isCurrentValidator[val.PublicKey.String()] = val } - isNextValidator := make(map[string]types.ConsumerValidator, len(nextValidators)) + isNextValidator := make(map[string]types.ConsensusValidator, len(nextValidators)) for _, val := range nextValidators { - isNextValidator[val.ConsumerPublicKey.String()] = val + isNextValidator[val.PublicKey.String()] = val } for _, currentVal := range currentValidators { - if nextVal, found := isNextValidator[currentVal.ConsumerPublicKey.String()]; !found { + if nextVal, found := isNextValidator[currentVal.PublicKey.String()]; !found { // this consumer public key does not appear in the next validators and hence we remove the validator // with that consumer public key by creating an update with 0 power - updates = append(updates, abci.ValidatorUpdate{PubKey: *currentVal.ConsumerPublicKey, Power: 0}) + updates = append(updates, abci.ValidatorUpdate{PubKey: *currentVal.PublicKey, Power: 0}) } else if currentVal.Power != nextVal.Power { // validator did not modify its consumer public key but has changed its voting power, so we // have to create an update with the new power - updates = append(updates, abci.ValidatorUpdate{PubKey: *nextVal.ConsumerPublicKey, Power: nextVal.Power}) + updates = append(updates, abci.ValidatorUpdate{PubKey: *nextVal.PublicKey, Power: nextVal.Power}) } // else no update is needed because neither the consumer public key changed, nor the power of the validator } for _, nextVal := range nextValidators { - if _, found := isCurrentValidator[nextVal.ConsumerPublicKey.String()]; !found { + if _, found := isCurrentValidator[nextVal.PublicKey.String()]; !found { // this consumer public key does not exist in the current validators and hence we introduce this validator - updates = append(updates, abci.ValidatorUpdate{PubKey: *nextVal.ConsumerPublicKey, Power: nextVal.Power}) + updates = append(updates, abci.ValidatorUpdate{PubKey: *nextVal.PublicKey, Power: nextVal.Power}) } } @@ -154,23 +122,27 @@ func DiffValidators( } // 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) { +func (k Keeper) CreateConsumerValidator(ctx sdk.Context, chainID string, validator stakingtypes.Validator) (types.ConsensusValidator, error) { valAddr, err := sdk.ValAddressFromBech32(validator.GetOperator()) if err != nil { - return types.ConsumerValidator{}, err + return types.ConsensusValidator{}, err } power, err := k.stakingKeeper.GetLastValidatorPower(ctx, valAddr) + if err != nil { + return types.ConsensusValidator{}, fmt.Errorf("could not retrieve validator's (%+v) power: %w", + validator, err) + } consAddr, err := validator.GetConsAddr() if err != nil { - return types.ConsumerValidator{}, fmt.Errorf("could not retrieve validator's (%+v) consensus address: %w", + return types.ConsensusValidator{}, 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, found := k.GetValidatorConsumerPubKey(ctx, chainID, types.NewProviderConsAddress(consAddr)) + if !found { 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.ConsensusValidator{}, fmt.Errorf("could not retrieve validator's (%+v) public key: %w", validator, err) } } @@ -181,11 +153,11 @@ func (k Keeper) CreateConsumerValidator(ctx sdk.Context, chainID string, validat height = v.JoinHeight } - return types.ConsumerValidator{ - ProviderConsAddr: consAddr, - Power: power, - ConsumerPublicKey: &consumerPublicKey, - JoinHeight: height, + return types.ConsensusValidator{ + ProviderConsAddr: consAddr, + Power: power, + PublicKey: &consumerPublicKey, + JoinHeight: height, }, nil } @@ -196,8 +168,8 @@ func (k Keeper) FilterValidators( chainID string, bondedValidators []stakingtypes.Validator, predicate func(providerAddr types.ProviderConsAddress) bool, -) []types.ConsumerValidator { - var nextValidators []types.ConsumerValidator +) []types.ConsensusValidator { + var nextValidators []types.ConsensusValidator for _, val := range bondedValidators { consAddr, err := val.GetConsAddr() if err != nil { @@ -224,5 +196,16 @@ func (k Keeper) FilterValidators( // 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, error) { - return ccv.GetLastBondedValidatorsUtil(ctx, k.stakingKeeper, k.Logger(ctx)) + maxVals, err := k.stakingKeeper.MaxValidators(ctx) + if err != nil { + return nil, err + } + return ccv.GetLastBondedValidatorsUtil(ctx, k.stakingKeeper, k.Logger(ctx), maxVals) +} + +// GetLastProviderConsensusActiveValidators returns the `MaxProviderConsensusValidators` many validators with the largest powers +// from the last bonded validators in the staking module. +func (k Keeper) GetLastProviderConsensusActiveValidators(ctx sdk.Context) ([]stakingtypes.Validator, error) { + maxVals := k.GetMaxProviderConsensusValidators(ctx) + return ccv.GetLastBondedValidatorsUtil(ctx, k.stakingKeeper, k.Logger(ctx), uint32(maxVals)) } diff --git a/x/ccv/provider/keeper/validator_set_update_test.go b/x/ccv/provider/keeper/validator_set_update_test.go index 43bc5a0370..e343093dad 100644 --- a/x/ccv/provider/keeper/validator_set_update_test.go +++ b/x/ccv/provider/keeper/validator_set_update_test.go @@ -26,10 +26,10 @@ func TestConsumerValidator(t *testing.T) { providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() - validator := types.ConsumerValidator{ - ProviderConsAddr: []byte("providerConsAddr"), - Power: 2, - ConsumerPublicKey: &crypto.PublicKey{}, + validator := types.ConsensusValidator{ + ProviderConsAddr: []byte("providerConsAddr"), + Power: 2, + PublicKey: &crypto.PublicKey{}, } require.False(t, providerKeeper.IsConsumerValidator(ctx, "chainID", types.NewProviderConsAddress(validator.ProviderConsAddr))) @@ -44,11 +44,11 @@ func TestGetConsumerValSet(t *testing.T) { defer ctrl.Finish() // create 3 validators and set them as current validators - expectedValidators := []types.ConsumerValidator{ + expectedValidators := []types.ConsensusValidator{ { ProviderConsAddr: []byte("providerConsAddr1"), Power: 1, - ConsumerPublicKey: &crypto.PublicKey{ + PublicKey: &crypto.PublicKey{ Sum: &crypto.PublicKey_Ed25519{ Ed25519: []byte{1}, }, @@ -57,7 +57,7 @@ func TestGetConsumerValSet(t *testing.T) { { ProviderConsAddr: []byte("providerConsAddr2"), Power: 2, - ConsumerPublicKey: &crypto.PublicKey{ + PublicKey: &crypto.PublicKey{ Sum: &crypto.PublicKey_Ed25519{ Ed25519: []byte{2}, }, @@ -66,7 +66,7 @@ func TestGetConsumerValSet(t *testing.T) { { ProviderConsAddr: []byte("providerConsAddr3"), Power: 3, - ConsumerPublicKey: &crypto.PublicKey{ + PublicKey: &crypto.PublicKey{ Sum: &crypto.PublicKey_Ed25519{ Ed25519: []byte{3}, }, @@ -76,17 +76,18 @@ func TestGetConsumerValSet(t *testing.T) { for _, expectedValidator := range expectedValidators { providerKeeper.SetConsumerValidator(ctx, "chainID", - types.ConsumerValidator{ - ProviderConsAddr: expectedValidator.ProviderConsAddr, - Power: expectedValidator.Power, - ConsumerPublicKey: expectedValidator.ConsumerPublicKey, + types.ConsensusValidator{ + ProviderConsAddr: expectedValidator.ProviderConsAddr, + Power: expectedValidator.Power, + PublicKey: expectedValidator.PublicKey, }) } - actualValidators := providerKeeper.GetConsumerValSet(ctx, "chainID") + actualValidators, err := providerKeeper.GetConsumerValSet(ctx, "chainID") + require.NoError(t, err) // sort validators first to be able to compare - sortValidators := func(validators []types.ConsumerValidator) { + sortValidators := func(validators []types.ConsensusValidator) { sort.Slice(validators, func(i, j int) bool { return bytes.Compare(validators[i].ProviderConsAddr, validators[j].ProviderConsAddr) < 0 }) @@ -99,13 +100,13 @@ func TestGetConsumerValSet(t *testing.T) { // createConsumerValidator is a helper function to create a consumer validator with the given `power`. It uses `index` as // the `ProviderConsAddr` of the validator, and the `seed` to generate the consumer public key. Returns the validator // and its consumer public key. -func createConsumerValidator(index int, power int64, seed int) (types.ConsumerValidator, crypto.PublicKey) { +func createConsumerValidator(index int, power int64, seed int) (types.ConsensusValidator, crypto.PublicKey) { publicKey := cryptotestutil.NewCryptoIdentityFromIntSeed(seed).TMProtoCryptoPublicKey() - return types.ConsumerValidator{ - ProviderConsAddr: []byte{byte(index)}, - Power: power, - ConsumerPublicKey: &publicKey, + return types.ConsensusValidator{ + ProviderConsAddr: []byte{byte(index)}, + Power: power, + PublicKey: &publicKey, }, publicKey } @@ -173,8 +174,8 @@ func TestDiff(t *testing.T) { nextF, nextPublicKeyF := createConsumerValidator(6, 1, 8) expectedUpdates = append(expectedUpdates, abci.ValidatorUpdate{PubKey: nextPublicKeyF, Power: 1}) - currentValidators := []types.ConsumerValidator{currentA, currentB, currentC, currentD, currentE} - nextValidators := []types.ConsumerValidator{nextB, nextC, nextD, nextE, nextF} + currentValidators := []types.ConsensusValidator{currentA, currentB, currentC, currentD, currentE} + nextValidators := []types.ConsensusValidator{nextB, nextC, nextD, nextE, nextF} actualUpdates := keeper.DiffValidators(currentValidators, nextValidators) @@ -194,12 +195,12 @@ func TestDiff(t *testing.T) { } func TestDiffEdgeCases(t *testing.T) { - require.Empty(t, len(keeper.DiffValidators([]types.ConsumerValidator{}, []types.ConsumerValidator{}))) + require.Empty(t, len(keeper.DiffValidators([]types.ConsensusValidator{}, []types.ConsensusValidator{}))) valA, publicKeyA := createConsumerValidator(1, 1, 1) valB, publicKeyB := createConsumerValidator(2, 2, 2) valC, publicKeyC := createConsumerValidator(3, 3, 3) - validators := []types.ConsumerValidator{valA, valB, valC} + validators := []types.ConsensusValidator{valA, valB, valC} // we do not expect any validator updates if the `currentValidators` are the same with the `nextValidators` require.Empty(t, len(keeper.DiffValidators(validators, validators))) @@ -210,7 +211,7 @@ func TestDiffEdgeCases(t *testing.T) { {PubKey: publicKeyB, Power: 2}, {PubKey: publicKeyC, Power: 3}, } - actualUpdates := keeper.DiffValidators([]types.ConsumerValidator{}, validators) + actualUpdates := keeper.DiffValidators([]types.ConsensusValidator{}, validators) // sort validators first to be able to compare sortUpdates := func(updates []abci.ValidatorUpdate) { sort.Slice(updates, func(i, j int) bool { @@ -231,7 +232,7 @@ func TestDiffEdgeCases(t *testing.T) { {PubKey: publicKeyB, Power: 0}, {PubKey: publicKeyC, Power: 0}, } - actualUpdates = keeper.DiffValidators(validators, []types.ConsumerValidator{}) + actualUpdates = keeper.DiffValidators(validators, []types.ConsensusValidator{}) sortUpdates(expectedUpdates) sortUpdates(actualUpdates) require.Equal(t, expectedUpdates, actualUpdates) @@ -254,11 +255,11 @@ func TestSetConsumerValSet(t *testing.T) { chainID := "chainID" - currentValidators := []types.ConsumerValidator{ + currentValidators := []types.ConsensusValidator{ { ProviderConsAddr: []byte("currentProviderConsAddr1"), Power: 2, - ConsumerPublicKey: &crypto.PublicKey{ + PublicKey: &crypto.PublicKey{ Sum: &crypto.PublicKey_Ed25519{ Ed25519: []byte{2}, }, @@ -267,7 +268,7 @@ func TestSetConsumerValSet(t *testing.T) { { ProviderConsAddr: []byte("currentProviderConsAddr2"), Power: 3, - ConsumerPublicKey: &crypto.PublicKey{ + PublicKey: &crypto.PublicKey{ Sum: &crypto.PublicKey_Ed25519{ Ed25519: []byte{3}, }, @@ -276,7 +277,7 @@ func TestSetConsumerValSet(t *testing.T) { { ProviderConsAddr: []byte("currentProviderConsAddr3"), Power: 4, - ConsumerPublicKey: &crypto.PublicKey{ + PublicKey: &crypto.PublicKey{ Sum: &crypto.PublicKey_Ed25519{ Ed25519: []byte{4}, }, @@ -284,11 +285,11 @@ func TestSetConsumerValSet(t *testing.T) { }, } - nextValidators := []types.ConsumerValidator{ + nextValidators := []types.ConsensusValidator{ { ProviderConsAddr: []byte("nextProviderConsAddr1"), Power: 2, - ConsumerPublicKey: &crypto.PublicKey{ + PublicKey: &crypto.PublicKey{ Sum: &crypto.PublicKey_Ed25519{ Ed25519: []byte{2}, }, @@ -297,7 +298,7 @@ func TestSetConsumerValSet(t *testing.T) { { ProviderConsAddr: []byte("nextProviderConsAddr2"), Power: 3, - ConsumerPublicKey: &crypto.PublicKey{ + PublicKey: &crypto.PublicKey{ Sum: &crypto.PublicKey_Ed25519{ Ed25519: []byte{3}, }, @@ -306,17 +307,23 @@ func TestSetConsumerValSet(t *testing.T) { } // set the `currentValidators` for chain `chainID` - require.Empty(t, providerKeeper.GetConsumerValSet(ctx, chainID)) + valSet, err := providerKeeper.GetConsumerValSet(ctx, chainID) + require.NoError(t, err) + require.Empty(t, valSet) for _, validator := range currentValidators { providerKeeper.SetConsumerValidator(ctx, chainID, validator) } - require.NotEmpty(t, providerKeeper.GetConsumerValSet(ctx, chainID)) + + valSet, err = providerKeeper.GetConsumerValSet(ctx, chainID) + require.NoError(t, err) + require.NotEmpty(t, valSet) providerKeeper.SetConsumerValSet(ctx, chainID, nextValidators) - nextCurrentValidators := providerKeeper.GetConsumerValSet(ctx, chainID) + nextCurrentValidators, err := providerKeeper.GetConsumerValSet(ctx, chainID) + require.NoError(t, err) // sort validators first to be able to compare - sortValidators := func(validators []types.ConsumerValidator) { + sortValidators := func(validators []types.ConsensusValidator) { sort.Slice(validators, func(i, j int) bool { return bytes.Compare(validators[i].ProviderConsAddr, validators[j].ProviderConsAddr) < 0 }) @@ -337,29 +344,29 @@ func TestFilterValidatorsConsiderAll(t *testing.T) { considerAll := func(providerAddr types.ProviderConsAddress) bool { return true } require.Empty(t, providerKeeper.FilterValidators(ctx, chainID, []stakingtypes.Validator{}, considerAll)) - var expectedValidators []types.ConsumerValidator + var expectedValidators []types.ConsensusValidator // create a staking validator A that has not set a consumer public key valA := createStakingValidator(ctx, mocks, 1, 1, 1) - // because validator A has no consumer key set, the `ConsumerPublicKey` we expect is the key on the provider chain + // because validator A has no consumer key set, the `PublicKey` 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, + expectedValidators = append(expectedValidators, types.ConsensusValidator{ + ProviderConsAddr: types.NewProviderConsAddress(valAConsAddr).Address.Bytes(), + Power: 1, + PublicKey: &valAPublicKey, }) // create a staking validator B that has set a consumer public key valB := createStakingValidator(ctx, mocks, 2, 2, 2) - // validator B has set a consumer key, the `ConsumerPublicKey` we expect is the key set by `SetValidatorConsumerPubKey` + // validator B has set a consumer key, the `PublicKey` 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, + expectedValidators = append(expectedValidators, types.ConsensusValidator{ + ProviderConsAddr: types.NewProviderConsAddress(valBConsAddr).Address.Bytes(), + Power: 2, + PublicKey: &valBConsumerKey, }) bondedValidators := []stakingtypes.Validator{valA, valB} @@ -379,30 +386,30 @@ func TestFilterValidatorsConsiderOnlyOptIn(t *testing.T) { return providerKeeper.IsOptedIn(ctx, chainID, providerAddr) })) - var expectedValidators []types.ConsumerValidator + var expectedValidators []types.ConsensusValidator // create a staking validator A that has not set a consumer public key valA := createStakingValidator(ctx, mocks, 1, 1, 1) - // because validator A has no consumer key set, the `ConsumerPublicKey` we expect is the key on the provider chain + // because validator A has no consumer key set, the `PublicKey` 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, + expectedValAConsumerValidator := types.ConsensusValidator{ + ProviderConsAddr: types.NewProviderConsAddress(valAConsAddr).Address.Bytes(), + Power: 1, + PublicKey: &valAPublicKey, } expectedValidators = append(expectedValidators, expectedValAConsumerValidator) // create a staking validator B that has set a consumer public key valB := createStakingValidator(ctx, mocks, 2, 2, 2) - // validator B has set a consumer key, the `ConsumerPublicKey` we expect is the key set by `SetValidatorConsumerPubKey` + // validator B has set a consumer key, the `PublicKey` 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, + expectedValBConsumerValidator := types.ConsensusValidator{ + ProviderConsAddr: types.NewProviderConsAddress(valBConsAddr).Address.Bytes(), + Power: 2, + PublicKey: &valBConsumerKey, } expectedValidators = append(expectedValidators, expectedValBConsumerValidator) @@ -418,7 +425,7 @@ func TestFilterValidatorsConsiderOnlyOptIn(t *testing.T) { }) // sort validators first to be able to compare - sortValidators := func(validators []types.ConsumerValidator) { + sortValidators := func(validators []types.ConsensusValidator) { sort.Slice(validators, func(i, j int) bool { return bytes.Compare(validators[i].ProviderConsAddr, validators[j].ProviderConsAddr) < 0 }) @@ -454,10 +461,10 @@ func TestCreateConsumerValidator(t *testing.T) { 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, + expectedConsumerValidatorA := types.ConsensusValidator{ + ProviderConsAddr: valAProviderConsAddr.ToSdkConsAddr(), + Power: 1, + PublicKey: &valAConsumerKey, } require.Equal(t, expectedConsumerValidatorA, actualConsumerValidatorA) require.NoError(t, err) @@ -468,10 +475,10 @@ func TestCreateConsumerValidator(t *testing.T) { valBProviderConsAddr := types.NewProviderConsAddress(valBConsAddr) valBPublicKey, _ := valB.TmConsPublicKey() actualConsumerValidatorB, err := providerKeeper.CreateConsumerValidator(ctx, chainID, valB) - expectedConsumerValidatorB := types.ConsumerValidator{ - ProviderConsAddr: valBProviderConsAddr.ToSdkConsAddr(), - Power: 2, - ConsumerPublicKey: &valBPublicKey, + expectedConsumerValidatorB := types.ConsensusValidator{ + ProviderConsAddr: valBProviderConsAddr.ToSdkConsAddr(), + Power: 2, + PublicKey: &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 b031f9705b..4678fe83cd 100644 --- a/x/ccv/provider/migrations/migrator.go +++ b/x/ccv/provider/migrations/migrator.go @@ -1,30 +1,38 @@ package migrations import ( + "fmt" + storetypes "cosmossdk.io/store/types" - "github.com/cosmos/cosmos-sdk/codec" 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" v6 "github.com/cosmos/interchain-security/v5/x/ccv/provider/migrations/v6" v7 "github.com/cosmos/interchain-security/v5/x/ccv/provider/migrations/v7" + v8 "github.com/cosmos/interchain-security/v5/x/ccv/provider/migrations/v8" ) // Migrator is a struct for handling in-place store migrations. type Migrator struct { providerKeeper providerkeeper.Keeper paramSpace paramtypes.Subspace - cdc codec.BinaryCodec storeKey storetypes.StoreKey } // NewMigrator returns a new Migrator. -func NewMigrator(providerKeeper providerkeeper.Keeper, paramSpace paramtypes.Subspace) Migrator { - return Migrator{providerKeeper: providerKeeper, paramSpace: paramSpace} +func NewMigrator( + providerKeeper providerkeeper.Keeper, + paramSpace paramtypes.Subspace, + storeKey storetypes.StoreKey, +) Migrator { + return Migrator{ + providerKeeper: providerKeeper, + paramSpace: paramSpace, + storeKey: storeKey, + } } // Migrating consensus version 1 to 2 is a no-op. @@ -32,12 +40,16 @@ func NewMigrator(providerKeeper providerkeeper.Keeper, paramSpace paramtypes.Sub // First run provider@v2.x.y in production to migrate from consensus version 1 to 2. // Then, in order to migrate to consensus version 3, first upgrade to provider@v3.x.y. func (m Migrator) Migrate1to2(ctx sdktypes.Context) error { - return nil + return fmt.Errorf("state migration failed: " + + "first run provider@v2.x.y in production to migrate from consensus version 1 to 2; " + + "then, in order to migrate to consensus version 3, first upgrade to provider@v3.x.y") } // Migrate2to3 migrates x/ccvprovider state from consensus version 2 to 3. +// First run provider@v4.0.x in production to migrate from consensus version 2 to 3. func (m Migrator) Migrate2to3(ctx sdktypes.Context) error { - return v3.MigrateQueuedPackets(ctx, m.providerKeeper) + return fmt.Errorf("state migration failed: " + + "first run provider@v4.0.x in production to migrate from consensus version 2 to 3") } // Migrate3to4 migrates x/ccvprovider state from consensus version 3 to 4. @@ -54,7 +66,8 @@ func (m Migrator) Migrate4to5(ctx sdktypes.Context) error { return nil } -// Migrate5to6 consists of setting the `NumberOfEpochsToStartReceivingRewards` param, as well as +// Migrate5to6 migrates x/ccvprovider state from consensus version 5 to 6. +// It 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) @@ -67,3 +80,19 @@ func (m Migrator) Migrate5to6(ctx sdktypes.Context) error { func (m Migrator) Migrate6to7(ctx sdktypes.Context) error { return v7.MigrateLegacyParams(ctx, m.providerKeeper, m.paramSpace) } + +// Migrate7to8 migrates x/ccvprovider state from consensus version 7 to 8. +// The migration consists of the following actions: +// - complete the outstanding paused unbonding ops waiting for VSCMaturedPackets from consumer chains +// - migrate the ConsumerAddrsToPrune index to ConsumerAddrsToPruneV2 +// - cleanup deprecated state +func (m Migrator) Migrate7to8(ctx sdktypes.Context) error { + store := ctx.KVStore(m.storeKey) + v8.CompleteUnbondingOps(ctx, store, m.providerKeeper) + if err := v8.MigrateConsumerAddrsToPrune(ctx, store, m.providerKeeper); err != nil { + return err + } + v8.CleanupState(store) + + return nil +} diff --git a/x/ccv/provider/migrations/v3/migration_test.go b/x/ccv/provider/migrations/v3/migration_test.go deleted file mode 100644 index 56d6b617d9..0000000000 --- a/x/ccv/provider/migrations/v3/migration_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package v3 - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - testutil "github.com/cosmos/interchain-security/v5/testutil/keeper" -) - -func TestMigrate2To3(t *testing.T) { - providerKeeper, ctx, ctrl, _ := testutil.GetProviderKeeperAndCtx(t, testutil.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - // Set consumer client ids to mock consumers being connected to provider - providerKeeper.SetConsumerClientId(ctx, "chain-1", "client-1") - providerKeeper.SetConsumerClientId(ctx, "chain-2", "client-2") - providerKeeper.SetConsumerClientId(ctx, "chain-3", "client-3") - - // Queue some data for chain-1 - providerKeeper.LegacyQueueThrottledPacketData( - ctx, "chain-1", 66, testutil.GetNewSlashPacketData()) - providerKeeper.LegacyQueueThrottledPacketData( - ctx, "chain-1", 67, testutil.GetNewVSCMaturedPacketData()) - providerKeeper.LegacyQueueThrottledPacketData( - ctx, "chain-1", 68, testutil.GetNewSlashPacketData()) - providerKeeper.LegacyQueueThrottledPacketData( - ctx, "chain-1", 69, testutil.GetNewVSCMaturedPacketData()) - - // Queue some data for chain-2 - providerKeeper.LegacyQueueThrottledPacketData( - ctx, "chain-2", 789, testutil.GetNewVSCMaturedPacketData()) - providerKeeper.LegacyQueueThrottledPacketData( - ctx, "chain-2", 790, testutil.GetNewSlashPacketData()) - providerKeeper.LegacyQueueThrottledPacketData( - ctx, "chain-2", 791, testutil.GetNewVSCMaturedPacketData()) - - // Queue some data for chain-3 - providerKeeper.LegacyQueueThrottledPacketData( - ctx, "chain-3", 123, testutil.GetNewSlashPacketData()) - providerKeeper.LegacyQueueThrottledPacketData( - ctx, "chain-3", 124, testutil.GetNewVSCMaturedPacketData()) - providerKeeper.LegacyQueueThrottledPacketData( - ctx, "chain-3", 125, testutil.GetNewVSCMaturedPacketData()) - - // Confirm getter methods return expected values - slash1, vscm1 := providerKeeper.LegacyGetAllThrottledPacketData(ctx, "chain-1") - require.Len(t, slash1, 2) - require.Len(t, vscm1, 2) - - slash2, vscm2 := providerKeeper.LegacyGetAllThrottledPacketData(ctx, "chain-2") - require.Len(t, slash2, 1) - require.Len(t, vscm2, 2) - - slash3, vscm3 := providerKeeper.LegacyGetAllThrottledPacketData(ctx, "chain-3") - require.Len(t, slash3, 1) - require.Len(t, vscm3, 2) - - // Set vsc send timestamp for every queued vsc matured packet, - // as a way to assert that the vsc matured packets are handled in the migration. - // - // That is, timestamp should exist before a vsc matured packet is handled, - // and deleted after handling. - for _, data := range vscm1 { - providerKeeper.SetVscSendTimestamp(ctx, "chain-1", data.ValsetUpdateId, time.Now()) - } - for _, data := range vscm2 { - providerKeeper.SetVscSendTimestamp(ctx, "chain-2", data.ValsetUpdateId, time.Now()) - } - for _, data := range vscm3 { - providerKeeper.SetVscSendTimestamp(ctx, "chain-3", data.ValsetUpdateId, time.Now()) - } - - // Confirm timestamps are set - for _, data := range vscm1 { - _, found := providerKeeper.GetVscSendTimestamp(ctx, "chain-1", data.ValsetUpdateId) - require.True(t, found) - } - for _, data := range vscm2 { - _, found := providerKeeper.GetVscSendTimestamp(ctx, "chain-2", data.ValsetUpdateId) - require.True(t, found) - } - for _, data := range vscm3 { - _, found := providerKeeper.GetVscSendTimestamp(ctx, "chain-3", data.ValsetUpdateId) - require.True(t, found) - } - - // Run migration - err := MigrateQueuedPackets(ctx, providerKeeper) - require.NoError(t, err) - - // Confirm throttled data is now deleted - slash1, vscm1 = providerKeeper.LegacyGetAllThrottledPacketData(ctx, "chain-1") - require.Empty(t, slash1) - require.Empty(t, vscm1) - slash2, vscm2 = providerKeeper.LegacyGetAllThrottledPacketData(ctx, "chain-2") - require.Empty(t, slash2) - require.Empty(t, vscm2) - slash3, vscm3 = providerKeeper.LegacyGetAllThrottledPacketData(ctx, "chain-3") - require.Empty(t, slash3) - require.Empty(t, vscm3) - - // Confirm timestamps are deleted, meaning vsc matured packets were handled - for _, data := range vscm1 { - _, found := providerKeeper.GetVscSendTimestamp(ctx, "chain-1", data.ValsetUpdateId) - require.False(t, found) - } - for _, data := range vscm2 { - _, found := providerKeeper.GetVscSendTimestamp(ctx, "chain-2", data.ValsetUpdateId) - require.False(t, found) - } - for _, data := range vscm3 { - _, found := providerKeeper.GetVscSendTimestamp(ctx, "chain-3", data.ValsetUpdateId) - require.False(t, found) - } -} diff --git a/x/ccv/provider/migrations/v3/migrations.go b/x/ccv/provider/migrations/v3/migrations.go deleted file mode 100644 index 8c17000b0b..0000000000 --- a/x/ccv/provider/migrations/v3/migrations.go +++ /dev/null @@ -1,25 +0,0 @@ -package v3 - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - - providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" -) - -// 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 _, 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, consumerChainID, data) - } - k.LegacyDeleteThrottledPacketDataForConsumer(ctx, consumerChainID) - } - return nil -} diff --git a/x/ccv/provider/migrations/v7/legacy_params.go b/x/ccv/provider/migrations/v7/legacy_params.go index a8c6d5218d..05e0cea59e 100644 --- a/x/ccv/provider/migrations/v7/legacy_params.go +++ b/x/ccv/provider/migrations/v7/legacy_params.go @@ -32,20 +32,6 @@ func getCCVTimeoutPeriod(ctx sdk.Context, paramSpace ccvtypes.LegacyParamSubspac return p } -// getInitTimeoutPeriod returns the init timeout period -func getInitTimeoutPeriod(ctx sdk.Context, paramSpace ccvtypes.LegacyParamSubspace) time.Duration { - var p time.Duration - paramSpace.Get(ctx, types.KeyInitTimeoutPeriod, &p) - return p -} - -// getVscTimeoutPeriod returns the vsc timeout period -func getVscTimeoutPeriod(ctx sdk.Context, paramSpace ccvtypes.LegacyParamSubspace) time.Duration { - var p time.Duration - paramSpace.Get(ctx, types.KeyVscTimeoutPeriod, &p) - return p -} - // getSlashMeterReplenishPeriod returns the period in which: // Once the slash meter becomes not-full, the slash meter is replenished after this period. func getSlashMeterReplenishPeriod(ctx sdk.Context, paramSpace ccvtypes.LegacyParamSubspace) time.Duration { @@ -88,12 +74,12 @@ func GetParamsLegacy(ctx sdk.Context, paramspace ccvtypes.LegacyParamSubspace) t getTemplateClient(ctx, paramspace), getTrustingPeriodFraction(ctx, paramspace), getCCVTimeoutPeriod(ctx, paramspace), - getInitTimeoutPeriod(ctx, paramspace), - getVscTimeoutPeriod(ctx, paramspace), getSlashMeterReplenishPeriod(ctx, paramspace), getSlashMeterReplenishFraction(ctx, paramspace), getConsumerRewardDenomRegistrationFee(ctx, paramspace), getBlocksPerEpoch(ctx, paramspace), getNumberOfEpochsToStartReceivingRewards(ctx, paramspace), + // this parameter is new so it doesn't need to be migrated, just initialized + types.DefaultMaxProviderConsensusValidators, ) } diff --git a/x/ccv/provider/migrations/v7/migrations_test.go b/x/ccv/provider/migrations/v7/migrations_test.go index f4ee763263..4b983205cc 100644 --- a/x/ccv/provider/migrations/v7/migrations_test.go +++ b/x/ccv/provider/migrations/v7/migrations_test.go @@ -23,8 +23,6 @@ func TestMigrateParams(t *testing.T) { inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyTemplateClient, defaultParams.TemplateClient) inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyTrustingPeriodFraction, defaultParams.TrustingPeriodFraction) inMemParams.ParamsSubspace.Set(ctx, ccvtypes.KeyCCVTimeoutPeriod, defaultParams.CcvTimeoutPeriod) - inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyInitTimeoutPeriod, defaultParams.InitTimeoutPeriod) - inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyVscTimeoutPeriod, defaultParams.VscTimeoutPeriod) inMemParams.ParamsSubspace.Set(ctx, providertypes.KeySlashMeterReplenishPeriod, defaultParams.SlashMeterReplenishPeriod) inMemParams.ParamsSubspace.Set(ctx, providertypes.KeySlashMeterReplenishFraction, defaultParams.SlashMeterReplenishFraction) inMemParams.ParamsSubspace.Set(ctx, providertypes.KeyConsumerRewardDenomRegistrationFee, defaultParams.ConsumerRewardDenomRegistrationFee) diff --git a/x/ccv/provider/migrations/v8/migrations.go b/x/ccv/provider/migrations/v8/migrations.go new file mode 100644 index 0000000000..cdf299f91f --- /dev/null +++ b/x/ccv/provider/migrations/v8/migrations.go @@ -0,0 +1,120 @@ +package v8 + +import ( + "encoding/binary" + "time" + + storetypes "cosmossdk.io/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + providerkeeper "github.com/cosmos/interchain-security/v5/x/ccv/provider/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" +) + +const ( + LegacyUnbondingOpBytePrefix = byte(11) + LegacyConsumerAddrsToPruneBytePrefix = byte(25) + LegacyMaturedUnbondingOpsByteKey = byte(1) + LegacyUnbondingOpIndexBytePrefix = byte(12) + LegacyInitTimeoutTimestampBytePrefix = byte(8) + LegacyVscSendTimestampBytePrefix = byte(18) + LegacyVSCMaturedHandledThisBlockBytePrefix = byte(28) +) + +// CompleteUnbondingOps completes all unbonding operations. +// Note that it must be executed before CleanupState. +func CompleteUnbondingOps(ctx sdk.Context, store storetypes.KVStore, pk providerkeeper.Keeper) { + iterator := storetypes.KVStorePrefixIterator(store, []byte{LegacyUnbondingOpBytePrefix}) + defer func() { + if err := iterator.Close(); err != nil { + pk.Logger(ctx).Error("Failed to close iterator", "error", err) + } + }() + + for ; iterator.Valid(); iterator.Next() { + id := binary.BigEndian.Uint64(iterator.Key()[1:]) + if err := pk.UnbondingCanComplete(ctx, id); err != nil { + pk.Logger(ctx).Error("UnbondingCanComplete failed", "unbondingID", id, "error", err.Error()) + } + } +} + +// MigrateConsumerAddrsToPrune migrates the ConsumerAddrsToPrune index to ConsumerAddrsToPruneV2. +// Note: This migration must be done before removing the VscSendTimestamp index +func MigrateConsumerAddrsToPrune(ctx sdk.Context, store storetypes.KVStore, pk providerkeeper.Keeper) error { + iterator := storetypes.KVStorePrefixIterator(store, []byte{LegacyConsumerAddrsToPruneBytePrefix}) + defer iterator.Close() + + unbondingPeriod, err := pk.UnbondingTime(ctx) + if err != nil { + return err + } + + for ; iterator.Valid(); iterator.Next() { + chainID, vscID, err := providertypes.ParseChainIdAndUintIdKey(LegacyConsumerAddrsToPruneBytePrefix, iterator.Key()) + if err != nil { + pk.Logger(ctx).Error("ParseChainIdAndUintIdKey failed while migrating ConsumerAddrsToPrune", + "key", string(iterator.Key()), + "error", err.Error(), + ) + continue + } + // use the VscSendTimestamp index to compute the timestamp after which this consumer address can be pruned + vscSendTimestampKey := providertypes.ChainIdAndUintIdKey(LegacyVscSendTimestampBytePrefix, chainID, vscID) + var sentTime time.Time + if timeBz := store.Get(vscSendTimestampKey); timeBz != nil { + if ts, err := sdk.ParseTimeBytes(timeBz); err == nil { + sentTime = ts + } else { + pk.Logger(ctx).Error("MigrateConsumerAddrsToPrune failed parsing VSC send timestamp key", "error", err.Error()) + continue + } + } else { + pk.Logger(ctx).Error( + "MigrateConsumerAddrsToPrune cannot find VSC send timestamp", + "chainID", chainID, + "vscID", vscID, + ) + continue + } + pruneAfterTs := sentTime.Add(unbondingPeriod).UTC() + + var addrs providertypes.AddressList + err = addrs.Unmarshal(iterator.Value()) + if err != nil { + pk.Logger(ctx).Error("MigrateConsumerAddrsToPrune failed unmarshaling data from store", "key", string(iterator.Value())) + continue + } + + for _, addr := range addrs.Addresses { + consumerAddr := providertypes.NewConsumerConsAddress(addr) + pk.AppendConsumerAddrsToPrune(ctx, chainID, pruneAfterTs, consumerAddr) + } + } + + return nil +} + +// CleanupState removes deprecated state +func CleanupState(store storetypes.KVStore) { + removePrefix(store, LegacyMaturedUnbondingOpsByteKey) + removePrefix(store, LegacyUnbondingOpBytePrefix) + removePrefix(store, LegacyUnbondingOpIndexBytePrefix) + removePrefix(store, LegacyInitTimeoutTimestampBytePrefix) + removePrefix(store, LegacyVscSendTimestampBytePrefix) + removePrefix(store, LegacyVSCMaturedHandledThisBlockBytePrefix) + removePrefix(store, LegacyConsumerAddrsToPruneBytePrefix) +} + +func removePrefix(store storetypes.KVStore, prefix byte) { + iterator := storetypes.KVStorePrefixIterator(store, []byte{prefix}) + defer iterator.Close() + + var keysToDel [][]byte + for ; iterator.Valid(); iterator.Next() { + keysToDel = append(keysToDel, iterator.Key()) + } + for _, delKey := range keysToDel { + store.Delete(delKey) + } +} diff --git a/x/ccv/provider/migrations/v8/migrations_test.go b/x/ccv/provider/migrations/v8/migrations_test.go new file mode 100644 index 0000000000..8c23fccc64 --- /dev/null +++ b/x/ccv/provider/migrations/v8/migrations_test.go @@ -0,0 +1,140 @@ +package v8 + +import ( + "testing" + "time" + + "github.com/golang/mock/gomock" + + storetypes "cosmossdk.io/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + testutil "github.com/cosmos/interchain-security/v5/testutil/keeper" + providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" + "github.com/stretchr/testify/require" +) + +func legacyConsumerAddrsToPruneKey(chainID string, vscID uint64) []byte { + return providertypes.ChainIdAndUintIdKey(LegacyConsumerAddrsToPruneBytePrefix, chainID, vscID) +} + +func legacyAppendConsumerAddrsToPrune( + store storetypes.KVStore, + chainID string, + vscID uint64, + consumerAddr providertypes.ConsumerConsAddress, +) { + bz := store.Get(legacyConsumerAddrsToPruneKey(chainID, vscID)) + var consumerAddrsToPrune providertypes.AddressList + if bz != nil { + err := consumerAddrsToPrune.Unmarshal(bz) + if err != nil { + // An error here would indicate something is very wrong, + // the data bytes are assumed to be correctly serialized by previous calls to this method. + panic(err) + } + } + consumerAddrsToPrune.Addresses = append(consumerAddrsToPrune.Addresses, consumerAddr.ToSdkConsAddr()) + bz, err := consumerAddrsToPrune.Marshal() + if err != nil { + // An error here would indicate something is very wrong, + // consumerAddrsToPrune is instantiated in this method and should be able to be marshaled. + panic(err) + } + store.Set(legacyConsumerAddrsToPruneKey(chainID, vscID), bz) +} + +func legacyVscSendingTimestampKey(chainID string, vscID uint64) []byte { + return providertypes.ChainIdAndUintIdKey(LegacyVscSendTimestampBytePrefix, chainID, vscID) +} + +func legacySetVscSendTimestamp( + store storetypes.KVStore, + chainID string, + vscID uint64, + timestamp time.Time, +) { + // Convert timestamp into bytes for storage + timeBz := sdk.FormatTimeBytes(timestamp) + + store.Set(legacyVscSendingTimestampKey(chainID, vscID), timeBz) +} + +func TestMigrateConsumerAddrsToPrune(t *testing.T) { + t.Helper() + inMemParams := testutil.NewInMemKeeperParams(t) + pk, ctx, ctrl, mocks := testutil.GetProviderKeeperAndCtx(t, inMemParams) + defer ctrl.Finish() + + store := ctx.KVStore(inMemParams.StoreKey) + + consumerAddrsToPrune := []struct { + chainId string + vscId uint64 + address providertypes.ConsumerConsAddress + }{ + {"chain-1", 1, providertypes.NewConsumerConsAddress([]byte{0x01})}, + {"chain-1", 1, providertypes.NewConsumerConsAddress([]byte{0x02})}, + {"chain-2", 1, providertypes.NewConsumerConsAddress([]byte{0x03})}, + {"chain-1", 2, providertypes.NewConsumerConsAddress([]byte{0x04})}, + {"chain-1", 3, providertypes.NewConsumerConsAddress([]byte{0x05})}, + } + for _, x := range consumerAddrsToPrune { + legacyAppendConsumerAddrsToPrune(store, x.chainId, x.vscId, x.address) + } + + ts1 := time.Now().UTC() + ts2 := ts1.Add(time.Minute).UTC() + ts3 := ts2.Add(time.Hour).UTC() + vscSendTimestamps := []struct { + chainId string + vscId uint64 + ts time.Time + }{ + {"chain-1", 1, ts1}, + {"chain-1", 2, ts2}, + {"chain-1", 3, ts3}, + {"chain-2", 1, ts1}, + } + for _, x := range vscSendTimestamps { + legacySetVscSendTimestamp(store, x.chainId, x.vscId, x.ts) + } + + gomock.InOrder( + mocks.MockStakingKeeper.EXPECT().UnbondingTime(ctx).Times(2), + ) + + unbondingPeriod, err := pk.UnbondingTime(ctx) + require.NoError(t, err) + + err = MigrateConsumerAddrsToPrune(ctx, store, pk) + require.NoError(t, err) + + consumerAddrs := pk.GetAllConsumerAddrsToPrune(ctx, "chain-1") + require.Len(t, consumerAddrs, 3) + // two addresses with ts1 + require.Equal(t, ts1.Add(unbondingPeriod).UTC(), consumerAddrs[0].PruneTs) + require.Len(t, consumerAddrs[0].ConsumerAddrs.Addresses, 2) + consumerAddr := providertypes.NewConsumerConsAddress(consumerAddrs[0].ConsumerAddrs.Addresses[0]) + require.Equal(t, consumerAddrsToPrune[0].address, consumerAddr) + consumerAddr = providertypes.NewConsumerConsAddress(consumerAddrs[0].ConsumerAddrs.Addresses[1]) + require.Equal(t, consumerAddrsToPrune[1].address, consumerAddr) + // one address with ts2 + require.Equal(t, ts2.Add(unbondingPeriod).UTC(), consumerAddrs[1].PruneTs) + require.Len(t, consumerAddrs[1].ConsumerAddrs.Addresses, 1) + consumerAddr = providertypes.NewConsumerConsAddress(consumerAddrs[1].ConsumerAddrs.Addresses[0]) + require.Equal(t, consumerAddrsToPrune[3].address, consumerAddr) + // one address with ts3 + require.Equal(t, ts3.Add(unbondingPeriod).UTC(), consumerAddrs[2].PruneTs) + require.Len(t, consumerAddrs[2].ConsumerAddrs.Addresses, 1) + consumerAddr = providertypes.NewConsumerConsAddress(consumerAddrs[2].ConsumerAddrs.Addresses[0]) + require.Equal(t, consumerAddrsToPrune[4].address, consumerAddr) + + consumerAddrs = pk.GetAllConsumerAddrsToPrune(ctx, "chain-2") + require.Len(t, consumerAddrs, 1) + // one address with ts1 + require.Equal(t, ts1.Add(unbondingPeriod).UTC(), consumerAddrs[0].PruneTs) + require.Len(t, consumerAddrs[0].ConsumerAddrs.Addresses, 1) + consumerAddr = providertypes.NewConsumerConsAddress(consumerAddrs[0].ConsumerAddrs.Addresses[0]) + require.Equal(t, consumerAddrsToPrune[2].address, consumerAddr) + +} diff --git a/x/ccv/provider/module.go b/x/ccv/provider/module.go index 8c87262640..320a135a69 100644 --- a/x/ccv/provider/module.go +++ b/x/ccv/provider/module.go @@ -5,10 +5,11 @@ import ( "encoding/json" "fmt" - "cosmossdk.io/core/appmodule" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" + "cosmossdk.io/core/appmodule" + storetypes "cosmossdk.io/store/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -17,9 +18,12 @@ import ( simtypes "github.com/cosmos/cosmos-sdk/types/simulation" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + 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" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/simulation" providertypes "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" ) @@ -27,14 +31,14 @@ var ( _ module.AppModule = (*AppModule)(nil) _ module.AppModuleBasic = (*AppModuleBasic)(nil) _ module.AppModuleSimulation = (*AppModule)(nil) - _ module.HasGenesis = (*AppModule)(nil) _ module.HasName = (*AppModule)(nil) _ module.HasConsensusVersion = (*AppModule)(nil) _ module.HasInvariants = (*AppModule)(nil) _ module.HasServices = (*AppModule)(nil) + _ module.HasABCIGenesis = (*AppModule)(nil) + _ module.HasABCIEndBlock = (*AppModule)(nil) _ appmodule.AppModule = (*AppModule)(nil) _ appmodule.HasBeginBlocker = (*AppModule)(nil) - _ appmodule.HasEndBlocker = (*AppModule)(nil) ) // AppModuleBasic is the IBC Provider AppModuleBasic @@ -101,19 +105,21 @@ type AppModule struct { AppModuleBasic keeper *keeper.Keeper paramSpace paramtypes.Subspace + storeKey storetypes.StoreKey } // NewAppModule creates a new provider module -func NewAppModule(k *keeper.Keeper, paramSpace paramtypes.Subspace) AppModule { +func NewAppModule(k *keeper.Keeper, paramSpace paramtypes.Subspace, storeKey storetypes.StoreKey) AppModule { return AppModule{ keeper: k, paramSpace: paramSpace, + storeKey: storeKey, } } // RegisterInvariants implements the AppModule interface -func (AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { - // TODO +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + keeper.RegisterInvariants(ir, am.keeper) } // RegisterServices registers module services. @@ -121,7 +127,7 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { providertypes.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) providertypes.RegisterQueryServer(cfg.QueryServer(), am.keeper) - migrator := migrations.NewMigrator(*am.keeper, am.paramSpace) + migrator := migrations.NewMigrator(*am.keeper, am.paramSpace, am.storeKey) if err := cfg.RegisterMigration(providertypes.ModuleName, 2, migrator.Migrate2to3); err != nil { panic(fmt.Sprintf("failed to register migrator for %s: %s -- from 2 -> 3", providertypes.ModuleName, err)) } @@ -132,21 +138,25 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { panic(fmt.Sprintf("failed to register migrator for %s: %s -- from 4 -> 5", providertypes.ModuleName, err)) } if err := cfg.RegisterMigration(providertypes.ModuleName, 5, migrator.Migrate5to6); err != nil { - panic(fmt.Sprintf("failed to register migrator for %s: %s", providertypes.ModuleName, err)) + panic(fmt.Sprintf("failed to register migrator for %s: %s -- from 5 -> 6", providertypes.ModuleName, err)) } if err := cfg.RegisterMigration(providertypes.ModuleName, 6, migrator.Migrate6to7); err != nil { - panic(fmt.Sprintf("failed to register migrator for %s: %s", providertypes.ModuleName, err)) + panic(fmt.Sprintf("failed to register migrator for %s: %s -- from 6 -> 7", providertypes.ModuleName, err)) + } + if err := cfg.RegisterMigration(providertypes.ModuleName, 7, migrator.Migrate7to8); err != nil { + panic(fmt.Sprintf("failed to register migrator for %s: %s -- from 7 -> 8", providertypes.ModuleName, err)) } } -// InitGenesis performs genesis initialization for the provider module. It returns no validator updates. +// InitGenesis performs genesis initialization for the provider module. It returns validator updates +// by selecting the first MaxProviderConsensusValidators from the staking module's validator set. // Note: This method along with ValidateGenesis satisfies the CCV spec: // https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/methods.md#ccv-pcf-initg1 -func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) { +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { var genesisState providertypes.GenesisState cdc.MustUnmarshalJSON(data, &genesisState) - am.keeper.InitGenesis(ctx, &genesisState) + return am.keeper.InitGenesis(ctx, &genesisState) } // ExportGenesis returns the exported genesis state as raw bytes for the provider @@ -157,7 +167,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw } // ConsensusVersion implements AppModule/ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 7 } +func (AppModule) ConsensusVersion() uint64 { return 8 } // BeginBlock implements the AppModule interface func (am AppModule) BeginBlock(ctx context.Context) error { @@ -175,24 +185,21 @@ func (am AppModule) BeginBlock(ctx context.Context) error { } // EndBlock implements the AppModule interface -func (am AppModule) EndBlock(ctx context.Context) error { +func (am AppModule) EndBlock(ctx context.Context) ([]abci.ValidatorUpdate, error) { sdkCtx := sdk.UnwrapSDKContext(ctx) // EndBlock logic needed for the Consumer Initiated Slashing sub-protocol. // Important: EndBlockCIS must be called before EndBlockVSU am.keeper.EndBlockCIS(sdkCtx) - // EndBlock logic needed for the Consumer Chain Removal sub-protocol - am.keeper.EndBlockCCR(sdkCtx) // EndBlock logic needed for the Validator Set Update sub-protocol - am.keeper.EndBlockVSU(sdkCtx) - - return nil + return am.keeper.EndBlockVSU(sdkCtx) } // AppModuleSimulation functions // GenerateGenesisState creates a randomized GenState of the transfer module. func (AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) } // RegisterStoreDecoder registers a decoder for provider module's types diff --git a/x/ccv/provider/module_test.go b/x/ccv/provider/module_test.go index b37c34d1da..2b2ac1c63f 100644 --- a/x/ccv/provider/module_test.go +++ b/x/ccv/provider/module_test.go @@ -3,14 +3,18 @@ package provider_test import ( "testing" - "cosmossdk.io/math" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "cosmossdk.io/math" + "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" "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" @@ -95,21 +99,17 @@ func TestInitGenesis(t *testing.T) { keeperParams := testkeeper.NewInMemKeeperParams(t) providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, keeperParams) - appModule := provider.NewAppModule(&providerKeeper, *keeperParams.ParamsSubspace) + appModule := provider.NewAppModule(&providerKeeper, *keeperParams.ParamsSubspace, keeperParams.StoreKey) genState := types.NewGenesisState( providerKeeper.GetValidatorSetUpdateId(ctx), nil, tc.consumerStates, nil, nil, - nil, - nil, types.DefaultParams(), nil, nil, nil, - nil, - nil, ) cdc := keeperParams.Cdc @@ -141,9 +141,19 @@ func TestInitGenesis(t *testing.T) { // Last total power is queried in InitGenesis, only if method has not // already panicked from unowned capability. if !tc.expPanic { + // create a mock validator + cId := crypto.NewCryptoIdentityFromIntSeed(234234) + validator := cId.SDKStakingValidator() + valAddr, err := sdk.ValAddressFromBech32(validator.GetOperator()) + require.NoError(t, err) + orderedCalls = append(orderedCalls, mocks.MockStakingKeeper.EXPECT().GetLastTotalPower( ctx).Return(math.NewInt(100), nil).Times(1), // Return total voting power as 100 + mocks.MockStakingKeeper.EXPECT().GetBondedValidatorsByPower( + ctx).Return([]stakingtypes.Validator{validator}, nil).Times(1), // Return a single validator + mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower( + ctx, valAddr).Return(int64(100), nil).Times(1), // Return total power as power of the single validator ) } diff --git a/x/ccv/provider/proposal_handler_test.go b/x/ccv/provider/proposal_handler_test.go index 2494da8c60..67bcad7d15 100644 --- a/x/ccv/provider/proposal_handler_test.go +++ b/x/ccv/provider/proposal_handler_test.go @@ -51,6 +51,8 @@ func TestProviderProposalHandler(t *testing.T) { 0, nil, nil, + 0, + false, ), blockTime: hourFromNow, // ctx blocktime is after proposal's spawn time expValidConsumerAddition: true, @@ -98,7 +100,7 @@ func TestProviderProposalHandler(t *testing.T) { // Mock expectations depending on expected outcome switch { case tc.expValidConsumerAddition: - testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 1, []stakingtypes.Validator{}, []int64{}, 1) + testkeeper.SetupMocksForLastBondedValidatorsExpectation(mocks.MockStakingKeeper, 1, []stakingtypes.Validator{}, 1) gomock.InOrder(testkeeper.GetMocksForCreateConsumerClient( ctx, &mocks, "chainID", clienttypes.NewHeight(2, 3), )...) diff --git a/x/ccv/provider/simulation/genesis.go b/x/ccv/provider/simulation/genesis.go new file mode 100644 index 0000000000..f27573cc7f --- /dev/null +++ b/x/ccv/provider/simulation/genesis.go @@ -0,0 +1,44 @@ +package simulation + +import ( + "encoding/json" + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/interchain-security/v5/x/ccv/provider/types" +) + +// Simulation parameter constants +const ( + // only includes params that make sense even with a single + maxProviderConsensusValidators = "max_provider_consensus_validators" +) + +// genMaxProviderConsensusValidators returns randomized maxProviderConsensusValidators +func genMaxProviderConsensusValidators(r *rand.Rand) int64 { + return int64(r.Intn(250) + 1) +} + +// RandomizedGenState generates a random GenesisState for staking +func RandomizedGenState(simState *module.SimulationState) { + // params + var ( + maxProviderConsensusVals int64 + ) + + simState.AppParams.GetOrGenerate(maxProviderConsensusValidators, &maxProviderConsensusVals, simState.Rand, func(r *rand.Rand) { maxProviderConsensusVals = genMaxProviderConsensusValidators(r) }) + + providerParams := types.DefaultParams() + providerParams.MaxProviderConsensusValidators = maxProviderConsensusVals + + providerGenesis := types.DefaultGenesisState() + providerGenesis.Params = providerParams + + bz, err := json.MarshalIndent(&providerGenesis.Params, "", " ") + if err != nil { + panic(err) + } + fmt.Printf("Selected randomly generated provider parameters:\n%s\n", bz) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(providerGenesis) +} diff --git a/x/ccv/provider/types/codec.go b/x/ccv/provider/types/codec.go index 1d75778bb1..a3593fb22f 100644 --- a/x/ccv/provider/types/codec.go +++ b/x/ccv/provider/types/codec.go @@ -1,13 +1,14 @@ package types import ( + "github.com/cosmos/ibc-go/v8/modules/core/exported" + tendermint "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" + "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - "github.com/cosmos/ibc-go/v8/modules/core/exported" - tendermint "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" ) func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { diff --git a/x/ccv/provider/types/consumer.go b/x/ccv/provider/types/consumer.go index 02651ac03e..37c286422f 100644 --- a/x/ccv/provider/types/consumer.go +++ b/x/ccv/provider/types/consumer.go @@ -10,7 +10,6 @@ func NewConsumerStates( channelID string, initialHeight uint64, genesis ccv.ConsumerGenesisState, - unbondingOpsIndexes []VscUnbondingOps, pendingValsetChanges []ccv.ValidatorSetChangePacketData, slashDowntimeAck []string, ) ConsumerState { @@ -19,7 +18,6 @@ func NewConsumerStates( ClientId: clientID, ChannelId: channelID, InitialHeight: initialHeight, - UnbondingOpsIndex: unbondingOpsIndexes, PendingValsetChanges: pendingValsetChanges, ConsumerGenesis: genesis, SlashDowntimeAck: slashDowntimeAck, diff --git a/x/ccv/provider/types/events.go b/x/ccv/provider/types/events.go index 8701821c52..9abcba7836 100644 --- a/x/ccv/provider/types/events.go +++ b/x/ccv/provider/types/events.go @@ -12,7 +12,6 @@ const ( EventTypeOptOut = "opt_out" AttributeInfractionHeight = "infraction_height" AttributeInitialHeight = "initial_height" - AttributeInitializationTimeout = "initialization_timeout" AttributeTrustingPeriod = "trusting_period" AttributeUnbondingPeriod = "unbonding_period" AttributeProviderValidatorAddress = "provider_validator_address" diff --git a/x/ccv/provider/types/genesis.go b/x/ccv/provider/types/genesis.go index 7ef643970b..5cd60982a6 100644 --- a/x/ccv/provider/types/genesis.go +++ b/x/ccv/provider/types/genesis.go @@ -16,31 +16,23 @@ func NewGenesisState( vscID uint64, vscIdToHeights []ValsetUpdateIdToHeight, consumerStates []ConsumerState, - unbondingOps []UnbondingOp, - matureUbdOps *MaturedUnbondingOps, additionProposals []ConsumerAdditionProposal, removalProposals []ConsumerRemovalProposal, params Params, validatorConsumerPubkeys []ValidatorConsumerPubKey, validatorsByConsumerAddr []ValidatorByConsumerAddr, - consumerAddrsToPrune []ConsumerAddrsToPrune, - initTimeoutTimestamps []InitTimeoutTimestamp, - exportedVscSendTimestamps []ExportedVscSendTimestamp, + consumerAddrsToPrune []ConsumerAddrsToPruneV2, ) *GenesisState { return &GenesisState{ ValsetUpdateId: vscID, ValsetUpdateIdToHeight: vscIdToHeights, ConsumerStates: consumerStates, - UnbondingOps: unbondingOps, - MatureUnbondingOps: matureUbdOps, ConsumerAdditionProposals: additionProposals, ConsumerRemovalProposals: removalProposals, Params: params, ValidatorConsumerPubkeys: validatorConsumerPubkeys, ValidatorsByConsumerAddr: validatorsByConsumerAddr, - ConsumerAddrsToPrune: consumerAddrsToPrune, - InitTimeoutTimestamps: initTimeoutTimestamps, - ExportedVscSendTimestamps: exportedVscSendTimestamps, + ConsumerAddrsToPruneV2: consumerAddrsToPrune, } } @@ -57,12 +49,6 @@ func (gs GenesisState) Validate() error { return errorsmod.Wrap(ccv.ErrInvalidGenesis, "valset update ID cannot be equal to zero") } - for _, ubdOp := range gs.UnbondingOps { - if err := gs.ValidateUnbondingOp(ubdOp); err != nil { - return err - } - } - for _, prop := range gs.ConsumerAdditionProposals { if err := prop.ValidateBasic(); err != nil { return errorsmod.Wrap(ccv.ErrInvalidGenesis, err.Error()) @@ -94,7 +80,7 @@ func (gs GenesisState) Validate() error { if err := KeyAssignmentValidateBasic(gs.ValidatorConsumerPubkeys, gs.ValidatorsByConsumerAddr, - gs.ConsumerAddrsToPrune, + gs.ConsumerAddrsToPruneV2, ); err != nil { return err } @@ -102,38 +88,6 @@ func (gs GenesisState) Validate() error { return nil } -func (gs GenesisState) ValidateUnbondingOp(ubdOp UnbondingOp) error { - if len(ubdOp.UnbondingConsumerChains) == 0 { - return errorsmod.Wrap(ccv.ErrInvalidGenesis, "unbonding operations cannot have an empty consumer chain list") - } - - // Check that the ID is set correctly in the UnbondingOpsIndex - for _, chainID := range ubdOp.UnbondingConsumerChains { - found := false - - // Find consumer state for this consumer chain - for _, cs := range gs.ConsumerStates { - if cs.ChainId != chainID { - continue - } - for _, vscUnbondingOps := range cs.UnbondingOpsIndex { - for _, id := range vscUnbondingOps.GetUnbondingOpIds() { - if id == ubdOp.Id { - found = true - break - } - } - } - } - if !found { - return errorsmod.Wrap(ccv.ErrInvalidGenesis, - fmt.Sprintf("unbonding operation without UnbondingOpsIndex, opID=%d, chainID=%s", ubdOp.Id, chainID)) - } - } - - return nil -} - // Validate performs a consumer state validation returning an error upon any failure. // It ensures that the chain id, client id and consumer genesis states are valid and non-empty. func (cs ConsumerState) Validate() error { @@ -167,15 +121,6 @@ func (cs ConsumerState) Validate() error { } } - for _, ubdOpIdx := range cs.UnbondingOpsIndex { - if ubdOpIdx.VscId == 0 { - return fmt.Errorf("UnbondingOpsIndex vscID cannot be equal to zero") - } - if len(ubdOpIdx.UnbondingOpIds) == 0 { - return fmt.Errorf("unbonding operation index cannot be empty: %#v", ubdOpIdx) - } - } - return nil } diff --git a/x/ccv/provider/types/genesis.pb.go b/x/ccv/provider/types/genesis.pb.go index d885dbc667..7097cd1a2b 100644 --- a/x/ccv/provider/types/genesis.pb.go +++ b/x/ccv/provider/types/genesis.pb.go @@ -31,10 +31,6 @@ type GenesisState struct { // empty for a new chain ConsumerStates []ConsumerState `protobuf:"bytes,2,rep,name=consumer_states,json=consumerStates,proto3" json:"consumer_states" yaml:"consumer_states"` // empty for a new chain - UnbondingOps []UnbondingOp `protobuf:"bytes,3,rep,name=unbonding_ops,json=unbondingOps,proto3" json:"unbonding_ops"` - // empty for a new chain - MatureUnbondingOps *MaturedUnbondingOps `protobuf:"bytes,4,opt,name=mature_unbonding_ops,json=matureUnbondingOps,proto3" json:"mature_unbonding_ops,omitempty"` - // empty for a new chain ValsetUpdateIdToHeight []ValsetUpdateIdToHeight `protobuf:"bytes,5,rep,name=valset_update_id_to_height,json=valsetUpdateIdToHeight,proto3" json:"valset_update_id_to_height"` // empty for a new chain ConsumerAdditionProposals []ConsumerAdditionProposal `protobuf:"bytes,6,rep,name=consumer_addition_proposals,json=consumerAdditionProposals,proto3" json:"consumer_addition_proposals"` @@ -46,9 +42,7 @@ type GenesisState struct { // empty for a new chain ValidatorsByConsumerAddr []ValidatorByConsumerAddr `protobuf:"bytes,10,rep,name=validators_by_consumer_addr,json=validatorsByConsumerAddr,proto3" json:"validators_by_consumer_addr"` // empty for a new chain - ConsumerAddrsToPrune []ConsumerAddrsToPrune `protobuf:"bytes,11,rep,name=consumer_addrs_to_prune,json=consumerAddrsToPrune,proto3" json:"consumer_addrs_to_prune"` - InitTimeoutTimestamps []InitTimeoutTimestamp `protobuf:"bytes,12,rep,name=init_timeout_timestamps,json=initTimeoutTimestamps,proto3" json:"init_timeout_timestamps"` - ExportedVscSendTimestamps []ExportedVscSendTimestamp `protobuf:"bytes,13,rep,name=exported_vsc_send_timestamps,json=exportedVscSendTimestamps,proto3" json:"exported_vsc_send_timestamps"` + ConsumerAddrsToPruneV2 []ConsumerAddrsToPruneV2 `protobuf:"bytes,14,rep,name=consumer_addrs_to_prune_v2,json=consumerAddrsToPruneV2,proto3" json:"consumer_addrs_to_prune_v2"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -98,20 +92,6 @@ func (m *GenesisState) GetConsumerStates() []ConsumerState { return nil } -func (m *GenesisState) GetUnbondingOps() []UnbondingOp { - if m != nil { - return m.UnbondingOps - } - return nil -} - -func (m *GenesisState) GetMatureUnbondingOps() *MaturedUnbondingOps { - if m != nil { - return m.MatureUnbondingOps - } - return nil -} - func (m *GenesisState) GetValsetUpdateIdToHeight() []ValsetUpdateIdToHeight { if m != nil { return m.ValsetUpdateIdToHeight @@ -154,23 +134,9 @@ func (m *GenesisState) GetValidatorsByConsumerAddr() []ValidatorByConsumerAddr { return nil } -func (m *GenesisState) GetConsumerAddrsToPrune() []ConsumerAddrsToPrune { +func (m *GenesisState) GetConsumerAddrsToPruneV2() []ConsumerAddrsToPruneV2 { if m != nil { - return m.ConsumerAddrsToPrune - } - return nil -} - -func (m *GenesisState) GetInitTimeoutTimestamps() []InitTimeoutTimestamp { - if m != nil { - return m.InitTimeoutTimestamps - } - return nil -} - -func (m *GenesisState) GetExportedVscSendTimestamps() []ExportedVscSendTimestamp { - if m != nil { - return m.ExportedVscSendTimestamps + return m.ConsumerAddrsToPruneV2 } return nil } @@ -193,9 +159,6 @@ type ConsumerState struct { // consumer chain PendingValsetChanges []types.ValidatorSetChangePacketData `protobuf:"bytes,6,rep,name=pending_valset_changes,json=pendingValsetChanges,proto3" json:"pending_valset_changes"` SlashDowntimeAck []string `protobuf:"bytes,7,rep,name=slash_downtime_ack,json=slashDowntimeAck,proto3" json:"slash_downtime_ack,omitempty"` - // UnbondingOpsIndex defines the unbonding operations waiting on this consumer - // chain - UnbondingOpsIndex []VscUnbondingOps `protobuf:"bytes,8,rep,name=unbonding_ops_index,json=unbondingOpsIndex,proto3" json:"unbonding_ops_index"` } func (m *ConsumerState) Reset() { *m = ConsumerState{} } @@ -280,13 +243,6 @@ func (m *ConsumerState) GetSlashDowntimeAck() []string { return nil } -func (m *ConsumerState) GetUnbondingOpsIndex() []VscUnbondingOps { - if m != nil { - return m.UnbondingOpsIndex - } - return nil -} - // ValsetUpdateIdToHeight defines the genesis information for the mapping // of each valset update id to a block height type ValsetUpdateIdToHeight struct { @@ -352,64 +308,56 @@ func init() { } var fileDescriptor_48411d9c7900d48e = []byte{ - // 908 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xdd, 0x6e, 0x1b, 0x45, - 0x14, 0xce, 0x26, 0x69, 0x1a, 0x4f, 0x7e, 0x08, 0x43, 0x70, 0x37, 0x09, 0xb8, 0x91, 0x51, 0xa5, - 0x48, 0x80, 0xdd, 0x04, 0x90, 0xca, 0x4f, 0x2f, 0x9a, 0x16, 0x81, 0x85, 0x10, 0x96, 0x93, 0x06, - 0xa9, 0x5c, 0x8c, 0xc6, 0x33, 0x23, 0x7b, 0x1a, 0x7b, 0x66, 0x35, 0x67, 0x76, 0x13, 0x0b, 0x21, - 0x15, 0xc1, 0x03, 0xf0, 0x58, 0xbd, 0xcc, 0x25, 0x57, 0x15, 0x4a, 0xde, 0x80, 0x27, 0x40, 0x3b, - 0x3b, 0xbb, 0x5d, 0x07, 0xa7, 0xb2, 0x7b, 0x95, 0x78, 0xbe, 0x39, 0xdf, 0xf7, 0x9d, 0x39, 0x33, - 0xe7, 0x2c, 0xda, 0x97, 0xca, 0x0a, 0xc3, 0xfa, 0x54, 0x2a, 0x02, 0x82, 0xc5, 0x46, 0xda, 0x51, - 0x93, 0xb1, 0xa4, 0x19, 0x19, 0x9d, 0x48, 0x2e, 0x4c, 0x33, 0xd9, 0x6f, 0xf6, 0x84, 0x12, 0x20, - 0xa1, 0x11, 0x19, 0x6d, 0x35, 0xfe, 0x68, 0x42, 0x48, 0x83, 0xb1, 0xa4, 0x91, 0x87, 0x34, 0x92, - 0xfd, 0xed, 0xcd, 0x9e, 0xee, 0x69, 0xb7, 0xbf, 0x99, 0xfe, 0x97, 0x85, 0x6e, 0xdf, 0xbf, 0x49, - 0x2d, 0xd9, 0x6f, 0x42, 0x9f, 0x1a, 0xc1, 0x09, 0xd3, 0x0a, 0xe2, 0xa1, 0x30, 0x3e, 0xe2, 0xde, - 0x1b, 0x22, 0xce, 0xa4, 0x11, 0x7e, 0xdb, 0xc1, 0x34, 0x69, 0x14, 0xfe, 0x5c, 0x4c, 0xfd, 0x62, - 0x05, 0xad, 0x7e, 0x97, 0x65, 0x76, 0x64, 0xa9, 0x15, 0x78, 0x0f, 0x6d, 0x24, 0x74, 0x00, 0xc2, - 0x92, 0x38, 0xe2, 0xd4, 0x0a, 0x22, 0x79, 0x18, 0xec, 0x06, 0x7b, 0x8b, 0x9d, 0xf5, 0x6c, 0xfd, - 0xa9, 0x5b, 0x6e, 0x71, 0xfc, 0x2b, 0x7a, 0x27, 0xf7, 0x49, 0x20, 0x8d, 0x85, 0x70, 0x7e, 0x77, - 0x61, 0x6f, 0xe5, 0xe0, 0xa0, 0x31, 0xc5, 0xe1, 0x34, 0x1e, 0xfb, 0x58, 0x27, 0x7b, 0x58, 0x7b, - 0xf9, 0xea, 0xee, 0xdc, 0xbf, 0xaf, 0xee, 0x56, 0x47, 0x74, 0x38, 0xf8, 0xaa, 0x7e, 0x8d, 0xb8, - 0xde, 0x59, 0x67, 0xe5, 0xed, 0x80, 0x7f, 0x41, 0x6b, 0xb1, 0xea, 0x6a, 0xc5, 0xa5, 0xea, 0x11, - 0x1d, 0x41, 0xb8, 0xe0, 0xa4, 0xef, 0x4f, 0x25, 0xfd, 0x34, 0x8f, 0xfc, 0x29, 0x3a, 0x5c, 0x4c, - 0x85, 0x3b, 0xab, 0xf1, 0xeb, 0x25, 0xc0, 0xcf, 0xd1, 0xe6, 0x90, 0xda, 0xd8, 0x08, 0x32, 0xae, - 0xb1, 0xb8, 0x1b, 0xec, 0xad, 0x1c, 0x3c, 0x98, 0x4a, 0xe3, 0x47, 0x47, 0xc0, 0x4b, 0x52, 0xd0, - 0xc1, 0x19, 0x6b, 0x79, 0x0d, 0xff, 0x86, 0xb6, 0xaf, 0x9f, 0x37, 0xb1, 0x9a, 0xf4, 0x85, 0xec, - 0xf5, 0x6d, 0x78, 0xcb, 0x65, 0xf5, 0xf5, 0x54, 0x8a, 0x27, 0x63, 0xe5, 0x39, 0xd6, 0xdf, 0x3b, - 0x0a, 0x9f, 0x60, 0x35, 0x99, 0x88, 0xe2, 0x3f, 0x02, 0xb4, 0x53, 0x1c, 0x36, 0xe5, 0x5c, 0x5a, - 0xa9, 0x15, 0x89, 0x8c, 0x8e, 0x34, 0xd0, 0x01, 0x84, 0x4b, 0xce, 0xc0, 0xc3, 0x99, 0x2a, 0xfa, - 0xc8, 0xd3, 0xb4, 0x3d, 0x8b, 0xb7, 0xb0, 0xc5, 0x6e, 0xc0, 0x01, 0xbf, 0x08, 0xd0, 0x76, 0xe1, - 0xc2, 0x88, 0xa1, 0x4e, 0xe8, 0xa0, 0x64, 0xe2, 0xb6, 0x33, 0xf1, 0xcd, 0x4c, 0x26, 0x3a, 0x19, - 0xcb, 0x35, 0x0f, 0x21, 0x9b, 0x0c, 0x03, 0x6e, 0xa1, 0xa5, 0x88, 0x1a, 0x3a, 0x84, 0x70, 0xd9, - 0x55, 0xf9, 0xe3, 0xa9, 0xd4, 0xda, 0x2e, 0xc4, 0x93, 0x7b, 0x02, 0x97, 0x4d, 0x42, 0x07, 0x92, - 0x53, 0xab, 0x4d, 0xf1, 0x96, 0x49, 0x14, 0x77, 0x4f, 0xc5, 0x08, 0xc2, 0xca, 0x0c, 0xd9, 0x9c, - 0xe4, 0x34, 0x79, 0x5a, 0xed, 0xb8, 0xfb, 0x83, 0x18, 0xe5, 0xd9, 0x24, 0x13, 0xe0, 0x54, 0x03, - 0xff, 0x1e, 0xa0, 0x9d, 0x02, 0x04, 0xd2, 0x1d, 0x91, 0x72, 0x91, 0x4d, 0x88, 0xde, 0xc6, 0xc3, - 0xe1, 0xa8, 0x54, 0x61, 0xf3, 0x3f, 0x0f, 0x30, 0x8e, 0xe3, 0x04, 0xdd, 0x19, 0x13, 0x85, 0xf4, - 0x5e, 0x47, 0x26, 0x56, 0x22, 0x5c, 0x71, 0xf2, 0x5f, 0xce, 0x7a, 0xab, 0x0c, 0x1c, 0xeb, 0x76, - 0x4a, 0xe0, 0xb5, 0x37, 0xd9, 0x04, 0x0c, 0x9f, 0xa1, 0x3b, 0x52, 0x49, 0x4b, 0xac, 0x1c, 0x0a, - 0x1d, 0x67, 0x7f, 0xc1, 0xd2, 0x61, 0x04, 0xe1, 0xea, 0x0c, 0xba, 0x2d, 0x25, 0xed, 0x71, 0x46, - 0x71, 0x9c, 0x33, 0x78, 0xdd, 0xf7, 0xe5, 0x04, 0x0c, 0xf0, 0x9f, 0x01, 0xfa, 0x40, 0x9c, 0x47, - 0xda, 0x58, 0xc1, 0x49, 0x02, 0x8c, 0x80, 0x50, 0xbc, 0x2c, 0xbf, 0x36, 0xc3, 0x63, 0xfa, 0xd6, - 0x13, 0x9d, 0x00, 0x3b, 0x12, 0x8a, 0x5f, 0xb7, 0xb0, 0x25, 0x6e, 0xc0, 0xa1, 0xfe, 0x62, 0x11, - 0xad, 0x8d, 0x35, 0x57, 0xbc, 0x85, 0x96, 0x33, 0x35, 0xdf, 0xcb, 0x2b, 0x9d, 0xdb, 0xee, 0x77, - 0x8b, 0xe3, 0x0f, 0x11, 0x62, 0x7d, 0xaa, 0x94, 0x18, 0xa4, 0xe0, 0xbc, 0x03, 0x2b, 0x7e, 0xa5, - 0xc5, 0xf1, 0x0e, 0xaa, 0xb0, 0x81, 0x14, 0xca, 0xa6, 0xe8, 0x82, 0x43, 0x97, 0xb3, 0x85, 0x16, - 0xc7, 0xf7, 0xd0, 0x7a, 0x7a, 0x10, 0x92, 0x0e, 0xf2, 0x76, 0xb5, 0xe8, 0x06, 0xc5, 0x9a, 0x5f, - 0xf5, 0x2d, 0x86, 0xa2, 0x8d, 0xe2, 0x1e, 0xf8, 0x21, 0x1a, 0xde, 0x72, 0x6f, 0xec, 0xe6, 0x6e, - 0x5d, 0xaa, 0x7b, 0x79, 0x3a, 0xf9, 0xe4, 0x8b, 0xb9, 0xe3, 0x31, 0x6c, 0x51, 0x35, 0x12, 0x59, - 0x9f, 0xf6, 0xcd, 0x34, 0x4d, 0xa1, 0x27, 0xf2, 0xfe, 0xf5, 0xe0, 0x4d, 0x42, 0xc5, 0xfd, 0x3e, - 0x12, 0xf6, 0xb1, 0x0b, 0x6b, 0x53, 0x76, 0x2a, 0xec, 0x13, 0x6a, 0x69, 0x7e, 0xd1, 0x3c, 0x7b, - 0xd6, 0x62, 0xb3, 0x4d, 0x80, 0x3f, 0x41, 0x18, 0x06, 0x14, 0xfa, 0x84, 0xeb, 0x33, 0x95, 0x96, - 0x99, 0x50, 0x76, 0xea, 0x9a, 0x55, 0xa5, 0xb3, 0xe1, 0x90, 0x27, 0x1e, 0x78, 0xc4, 0x4e, 0xf1, - 0x73, 0xf4, 0xde, 0xd8, 0x34, 0x21, 0x52, 0x71, 0x71, 0x1e, 0x2e, 0x3b, 0x83, 0x9f, 0x4f, 0xf7, - 0x12, 0x81, 0x95, 0x67, 0x87, 0x37, 0xf7, 0x6e, 0x79, 0x76, 0xb5, 0x52, 0xd2, 0xfa, 0x33, 0x54, - 0x9d, 0x3c, 0x0d, 0x66, 0x18, 0xef, 0x55, 0xb4, 0xe4, 0xab, 0x3a, 0xef, 0x70, 0xff, 0xeb, 0xf0, - 0xe7, 0x97, 0x97, 0xb5, 0xe0, 0xe2, 0xb2, 0x16, 0xfc, 0x73, 0x59, 0x0b, 0xfe, 0xba, 0xaa, 0xcd, - 0x5d, 0x5c, 0xd5, 0xe6, 0xfe, 0xbe, 0xaa, 0xcd, 0x3d, 0x7b, 0xd8, 0x93, 0xb6, 0x1f, 0x77, 0x1b, - 0x4c, 0x0f, 0x9b, 0x4c, 0xc3, 0x50, 0x43, 0xf3, 0x75, 0x56, 0x9f, 0x16, 0x5f, 0x24, 0xc9, 0x17, - 0xcd, 0xf3, 0xf1, 0xcf, 0x12, 0x3b, 0x8a, 0x04, 0x74, 0x97, 0xdc, 0x17, 0xc9, 0x67, 0xff, 0x05, - 0x00, 0x00, 0xff, 0xff, 0xae, 0xb1, 0x22, 0x59, 0x8e, 0x09, 0x00, 0x00, + // 782 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xd1, 0x6e, 0xda, 0x48, + 0x14, 0xc5, 0xc1, 0x21, 0x66, 0x12, 0x58, 0xcb, 0x8a, 0x90, 0x43, 0xb4, 0x24, 0x62, 0x15, 0x09, + 0x69, 0x77, 0x71, 0x60, 0xb5, 0xd2, 0x6a, 0x77, 0xf3, 0x10, 0x12, 0x69, 0x17, 0xfa, 0x82, 0x48, + 0x9a, 0x4a, 0x79, 0xb1, 0x86, 0xf1, 0x08, 0x46, 0x18, 0x8f, 0xe5, 0x19, 0x9c, 0xa2, 0xaa, 0x52, + 0xab, 0xfe, 0x40, 0x9f, 0xfb, 0x0d, 0xfd, 0x90, 0x3c, 0xe6, 0xb1, 0x4f, 0x51, 0x95, 0xfc, 0x41, + 0xbf, 0xa0, 0xf2, 0x78, 0xec, 0x42, 0x4a, 0x22, 0xe8, 0x1b, 0xcc, 0x99, 0x73, 0xee, 0xb9, 0xd7, + 0xf7, 0xde, 0x01, 0x0d, 0xe2, 0x71, 0x1c, 0xa0, 0x21, 0x24, 0x9e, 0xcd, 0x30, 0x9a, 0x04, 0x84, + 0x4f, 0x2d, 0x84, 0x42, 0xcb, 0x0f, 0x68, 0x48, 0x1c, 0x1c, 0x58, 0x61, 0xc3, 0x1a, 0x60, 0x0f, + 0x33, 0xc2, 0xea, 0x7e, 0x40, 0x39, 0x35, 0x7e, 0x59, 0x40, 0xa9, 0x23, 0x14, 0xd6, 0x13, 0x4a, + 0x3d, 0x6c, 0x94, 0xb7, 0x07, 0x74, 0x40, 0xc5, 0x7d, 0x2b, 0xfa, 0x15, 0x53, 0xcb, 0x87, 0x8f, + 0x45, 0x0b, 0x1b, 0x16, 0x1b, 0xc2, 0x00, 0x3b, 0x36, 0xa2, 0x1e, 0x9b, 0x8c, 0x71, 0x20, 0x19, + 0x07, 0x4f, 0x30, 0xae, 0x48, 0x80, 0xe5, 0xb5, 0xe6, 0x32, 0x69, 0xa4, 0xfe, 0x04, 0xa7, 0xfa, + 0x51, 0x03, 0x5b, 0xff, 0xc5, 0x99, 0x9d, 0x71, 0xc8, 0xb1, 0x51, 0x03, 0x7a, 0x08, 0x5d, 0x86, + 0xb9, 0x3d, 0xf1, 0x1d, 0xc8, 0xb1, 0x4d, 0x1c, 0x53, 0xd9, 0x57, 0x6a, 0x6a, 0xaf, 0x18, 0x9f, + 0x3f, 0x17, 0xc7, 0x6d, 0xc7, 0x78, 0x05, 0x7e, 0x4a, 0x7c, 0xda, 0x2c, 0xe2, 0x32, 0x73, 0x6d, + 0x3f, 0x5b, 0xdb, 0x6c, 0x36, 0xeb, 0x4b, 0x14, 0xa7, 0x7e, 0x22, 0xb9, 0x22, 0x6c, 0xab, 0x72, + 0x7d, 0xbb, 0x97, 0xf9, 0x72, 0xbb, 0x57, 0x9a, 0xc2, 0xb1, 0xfb, 0x77, 0xf5, 0x81, 0x70, 0xb5, + 0x57, 0x44, 0xb3, 0xd7, 0x99, 0xf1, 0x1a, 0x94, 0x1f, 0xda, 0xb4, 0x39, 0xb5, 0x87, 0x98, 0x0c, + 0x86, 0xdc, 0x5c, 0x17, 0x3e, 0xfe, 0x59, 0xca, 0xc7, 0xc5, 0x5c, 0x56, 0xe7, 0xf4, 0x7f, 0x21, + 0xd1, 0x52, 0x23, 0x43, 0xbd, 0x52, 0xb8, 0x10, 0x35, 0xde, 0x29, 0x60, 0x37, 0xf5, 0x08, 0x1d, + 0x87, 0x70, 0x42, 0x3d, 0xdb, 0x0f, 0xa8, 0x4f, 0x19, 0x74, 0x99, 0x99, 0x13, 0x06, 0x8e, 0x56, + 0x2a, 0xc4, 0xb1, 0x94, 0xe9, 0x4a, 0x15, 0x69, 0x61, 0x07, 0x3d, 0x82, 0x33, 0xe3, 0x8d, 0x02, + 0xca, 0xa9, 0x8b, 0x00, 0x8f, 0x69, 0x08, 0xdd, 0x19, 0x13, 0x1b, 0xc2, 0xc4, 0xbf, 0x2b, 0x99, + 0xe8, 0xc5, 0x2a, 0x0f, 0x3c, 0x98, 0x68, 0x31, 0xcc, 0x8c, 0x36, 0xc8, 0xf9, 0x30, 0x80, 0x63, + 0x66, 0x6a, 0xfb, 0x4a, 0x6d, 0xb3, 0xf9, 0xeb, 0x52, 0xd1, 0xba, 0x82, 0x22, 0xc5, 0xa5, 0x80, + 0xc8, 0x26, 0x84, 0x2e, 0x71, 0x20, 0xa7, 0x41, 0x3a, 0x02, 0xb6, 0x3f, 0xe9, 0x8f, 0xf0, 0x94, + 0x99, 0xf9, 0x15, 0xb2, 0xb9, 0x48, 0x64, 0x92, 0xb4, 0xba, 0x93, 0xfe, 0x33, 0x3c, 0x4d, 0xb2, + 0x09, 0x17, 0xc0, 0x51, 0x0c, 0xe3, 0xad, 0x02, 0x76, 0x53, 0x90, 0xd9, 0xfd, 0xa9, 0x3d, 0xfb, + 0x91, 0x03, 0x13, 0xfc, 0x88, 0x87, 0xd6, 0x74, 0xe6, 0x0b, 0x07, 0xdf, 0x79, 0x60, 0xf3, 0x78, + 0xd4, 0xd9, 0x73, 0x41, 0x59, 0xd4, 0xd7, 0x7e, 0x30, 0xf1, 0xb0, 0x1d, 0x36, 0xcd, 0xe2, 0x0a, + 0x9d, 0x3d, 0x2b, 0xcb, 0xce, 0x69, 0x37, 0xd2, 0xb8, 0x68, 0x26, 0x9d, 0x8d, 0x16, 0xa2, 0x1d, + 0x55, 0xcb, 0xea, 0x6a, 0x47, 0xd5, 0x54, 0x7d, 0xbd, 0xa3, 0x6a, 0x9b, 0xfa, 0x56, 0x47, 0xd5, + 0xb6, 0xf4, 0x42, 0x47, 0xd5, 0x0a, 0x7a, 0xb1, 0xfa, 0x21, 0x0b, 0x0a, 0x73, 0x83, 0x6b, 0xec, + 0x00, 0x2d, 0xb6, 0x21, 0xf7, 0x44, 0xbe, 0xb7, 0x21, 0xfe, 0xb7, 0x1d, 0xe3, 0x67, 0x00, 0xd0, + 0x10, 0x7a, 0x1e, 0x76, 0x23, 0x70, 0x4d, 0x80, 0x79, 0x79, 0xd2, 0x76, 0x8c, 0x5d, 0x90, 0x47, + 0x2e, 0xc1, 0x1e, 0x8f, 0xd0, 0xac, 0x40, 0xb5, 0xf8, 0xa0, 0xed, 0x18, 0x07, 0xa0, 0x48, 0x3c, + 0xc2, 0x09, 0x74, 0x93, 0x99, 0x56, 0xc5, 0x12, 0x2a, 0xc8, 0x53, 0x39, 0x87, 0x10, 0xe8, 0x69, + 0xb1, 0xe4, 0x82, 0x36, 0xd7, 0x45, 0x23, 0x1e, 0x3e, 0x5a, 0xa2, 0x99, 0xca, 0xcc, 0x6e, 0x3e, + 0x59, 0x97, 0x74, 0xa7, 0x49, 0xcc, 0xe0, 0xa0, 0xe4, 0x63, 0xcf, 0x21, 0xde, 0xc0, 0x96, 0x1b, + 0x27, 0x4a, 0x61, 0x80, 0x93, 0x21, 0xff, 0xeb, 0xa9, 0x40, 0x69, 0x13, 0x9c, 0x61, 0x7e, 0x22, + 0x68, 0x5d, 0x88, 0x46, 0x98, 0x9f, 0x42, 0x0e, 0x65, 0xc0, 0x6d, 0xa9, 0x1e, 0xef, 0xa1, 0xf8, + 0x12, 0x33, 0x7e, 0x03, 0x06, 0x73, 0x21, 0x1b, 0xda, 0x0e, 0xbd, 0xf2, 0x38, 0x19, 0x63, 0x1b, + 0xa2, 0x91, 0x98, 0xe8, 0x7c, 0x4f, 0x17, 0xc8, 0xa9, 0x04, 0x8e, 0xd1, 0xa8, 0xa3, 0x6a, 0x9a, + 0x9e, 0xaf, 0x5e, 0x82, 0xd2, 0xe2, 0x65, 0xb6, 0xc2, 0x52, 0x2f, 0x81, 0x9c, 0xac, 0xf7, 0x9a, + 0xc0, 0xe5, 0xbf, 0xd6, 0x8b, 0xeb, 0xbb, 0x8a, 0x72, 0x73, 0x57, 0x51, 0x3e, 0xdf, 0x55, 0x94, + 0xf7, 0xf7, 0x95, 0xcc, 0xcd, 0x7d, 0x25, 0xf3, 0xe9, 0xbe, 0x92, 0xb9, 0x3c, 0x1a, 0x10, 0x3e, + 0x9c, 0xf4, 0xeb, 0x88, 0x8e, 0x2d, 0x44, 0xd9, 0x98, 0x32, 0xeb, 0x5b, 0x41, 0x7e, 0x4f, 0xdf, + 0xa1, 0xf0, 0x4f, 0xeb, 0xe5, 0xfc, 0x63, 0xc4, 0xa7, 0x3e, 0x66, 0xfd, 0x9c, 0x78, 0x87, 0xfe, + 0xf8, 0x1a, 0x00, 0x00, 0xff, 0xff, 0xa4, 0x95, 0x75, 0xdb, 0x84, 0x07, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -432,10 +380,10 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if len(m.ExportedVscSendTimestamps) > 0 { - for iNdEx := len(m.ExportedVscSendTimestamps) - 1; iNdEx >= 0; iNdEx-- { + if len(m.ConsumerAddrsToPruneV2) > 0 { + for iNdEx := len(m.ConsumerAddrsToPruneV2) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.ExportedVscSendTimestamps[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.ConsumerAddrsToPruneV2[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -443,35 +391,7 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintGenesis(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x6a - } - } - if len(m.InitTimeoutTimestamps) > 0 { - for iNdEx := len(m.InitTimeoutTimestamps) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.InitTimeoutTimestamps[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenesis(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x62 - } - } - if len(m.ConsumerAddrsToPrune) > 0 { - for iNdEx := len(m.ConsumerAddrsToPrune) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.ConsumerAddrsToPrune[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenesis(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x5a + dAtA[i] = 0x72 } } if len(m.ValidatorsByConsumerAddr) > 0 { @@ -554,32 +474,6 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x2a } } - if m.MatureUnbondingOps != nil { - { - size, err := m.MatureUnbondingOps.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenesis(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x22 - } - if len(m.UnbondingOps) > 0 { - for iNdEx := len(m.UnbondingOps) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.UnbondingOps[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenesis(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a - } - } if len(m.ConsumerStates) > 0 { for iNdEx := len(m.ConsumerStates) - 1; iNdEx >= 0; iNdEx-- { { @@ -622,20 +516,6 @@ func (m *ConsumerState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if len(m.UnbondingOpsIndex) > 0 { - for iNdEx := len(m.UnbondingOpsIndex) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.UnbondingOpsIndex[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenesis(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x42 - } - } if len(m.SlashDowntimeAck) > 0 { for iNdEx := len(m.SlashDowntimeAck) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.SlashDowntimeAck[iNdEx]) @@ -757,16 +637,6 @@ func (m *GenesisState) Size() (n int) { n += 1 + l + sovGenesis(uint64(l)) } } - if len(m.UnbondingOps) > 0 { - for _, e := range m.UnbondingOps { - l = e.Size() - n += 1 + l + sovGenesis(uint64(l)) - } - } - if m.MatureUnbondingOps != nil { - l = m.MatureUnbondingOps.Size() - n += 1 + l + sovGenesis(uint64(l)) - } if len(m.ValsetUpdateIdToHeight) > 0 { for _, e := range m.ValsetUpdateIdToHeight { l = e.Size() @@ -799,20 +669,8 @@ func (m *GenesisState) Size() (n int) { n += 1 + l + sovGenesis(uint64(l)) } } - if len(m.ConsumerAddrsToPrune) > 0 { - for _, e := range m.ConsumerAddrsToPrune { - l = e.Size() - n += 1 + l + sovGenesis(uint64(l)) - } - } - if len(m.InitTimeoutTimestamps) > 0 { - for _, e := range m.InitTimeoutTimestamps { - l = e.Size() - n += 1 + l + sovGenesis(uint64(l)) - } - } - if len(m.ExportedVscSendTimestamps) > 0 { - for _, e := range m.ExportedVscSendTimestamps { + if len(m.ConsumerAddrsToPruneV2) > 0 { + for _, e := range m.ConsumerAddrsToPruneV2 { l = e.Size() n += 1 + l + sovGenesis(uint64(l)) } @@ -855,12 +713,6 @@ func (m *ConsumerState) Size() (n int) { n += 1 + l + sovGenesis(uint64(l)) } } - if len(m.UnbondingOpsIndex) > 0 { - for _, e := range m.UnbondingOpsIndex { - l = e.Size() - n += 1 + l + sovGenesis(uint64(l)) - } - } return n } @@ -967,76 +819,6 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field UnbondingOps", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenesis - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenesis - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenesis - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.UnbondingOps = append(m.UnbondingOps, UnbondingOp{}) - if err := m.UnbondingOps[len(m.UnbondingOps)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field MatureUnbondingOps", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenesis - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenesis - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenesis - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.MatureUnbondingOps == nil { - m.MatureUnbondingOps = &MaturedUnbondingOps{} - } - if err := m.MatureUnbondingOps.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ValsetUpdateIdToHeight", wireType) @@ -1240,43 +1022,9 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 11: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ConsumerAddrsToPrune", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenesis - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenesis - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenesis - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ConsumerAddrsToPrune = append(m.ConsumerAddrsToPrune, ConsumerAddrsToPrune{}) - if err := m.ConsumerAddrsToPrune[len(m.ConsumerAddrsToPrune)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 12: + case 14: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field InitTimeoutTimestamps", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ConsumerAddrsToPruneV2", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1303,42 +1051,8 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.InitTimeoutTimestamps = append(m.InitTimeoutTimestamps, InitTimeoutTimestamp{}) - if err := m.InitTimeoutTimestamps[len(m.InitTimeoutTimestamps)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 13: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ExportedVscSendTimestamps", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenesis - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenesis - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenesis - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ExportedVscSendTimestamps = append(m.ExportedVscSendTimestamps, ExportedVscSendTimestamp{}) - if err := m.ExportedVscSendTimestamps[len(m.ExportedVscSendTimestamps)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.ConsumerAddrsToPruneV2 = append(m.ConsumerAddrsToPruneV2, ConsumerAddrsToPruneV2{}) + if err := m.ConsumerAddrsToPruneV2[len(m.ConsumerAddrsToPruneV2)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1606,40 +1320,6 @@ func (m *ConsumerState) Unmarshal(dAtA []byte) error { } m.SlashDowntimeAck = append(m.SlashDowntimeAck, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 8: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field UnbondingOpsIndex", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenesis - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenesis - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenesis - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.UnbondingOpsIndex = append(m.UnbondingOpsIndex, VscUnbondingOps{}) - if err := m.UnbondingOpsIndex[len(m.UnbondingOpsIndex)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) diff --git a/x/ccv/provider/types/genesis_test.go b/x/ccv/provider/types/genesis_test.go index 6a9e5a7593..c5472e07d5 100644 --- a/x/ccv/provider/types/genesis_test.go +++ b/x/ccv/provider/types/genesis_test.go @@ -35,14 +35,10 @@ func TestValidateGenesisState(t *testing.T) { []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-1")}}, nil, nil, - nil, - nil, types.DefaultParams(), nil, nil, nil, - nil, - nil, ), true, }, @@ -59,14 +55,10 @@ func TestValidateGenesisState(t *testing.T) { }, nil, nil, - nil, - nil, types.DefaultParams(), nil, nil, nil, - nil, - nil, ), true, }, @@ -78,13 +70,9 @@ func TestValidateGenesisState(t *testing.T) { []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-1")}}, nil, nil, - nil, - 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: math.NewInt(10000000)}, 600, 24), - nil, - nil, + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24, 180), nil, nil, nil, @@ -99,13 +87,9 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, - nil, - 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: math.NewInt(10000000)}, 600, 24), - nil, - nil, + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24, 180), nil, nil, nil, @@ -120,34 +104,9 @@ func TestValidateGenesisState(t *testing.T) { nil, nil, nil, - nil, - 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: math.NewInt(10000000)}, 600, 24), - nil, - nil, - nil, - nil, - nil, - ), - false, - }, - { - "invalid unbonding op", - types.NewGenesisState( - types.DefaultValsetUpdateID, - nil, - nil, - []types.UnbondingOp{{UnbondingConsumerChains: nil}}, - nil, - nil, - 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: math.NewInt(10000000)}, 600, 24), - nil, - nil, + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24, 180), nil, nil, nil, @@ -162,19 +121,13 @@ func TestValidateGenesisState(t *testing.T) { []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id"}}, nil, nil, - nil, - nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, 0, clienttypes.Height{}, nil, []string{"ibc", "upgradedIBCState"}), types.DefaultTrustingPeriodFraction, ccv.DefaultCCVTimeoutPeriod, - types.DefaultInitTimeoutPeriod, - types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24), - nil, - nil, + sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24, 180), nil, nil, nil, @@ -189,19 +142,13 @@ func TestValidateGenesisState(t *testing.T) { []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id"}}, nil, nil, - nil, - nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), "0.0", // 0 trusting period fraction here ccv.DefaultCCVTimeoutPeriod, - types.DefaultInitTimeoutPeriod, - types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24), - nil, - nil, + sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24, 180), nil, nil, nil, @@ -216,73 +163,13 @@ func TestValidateGenesisState(t *testing.T) { []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id"}}, nil, nil, - nil, - nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), types.DefaultTrustingPeriodFraction, 0, // 0 ccv timeout here - types.DefaultInitTimeoutPeriod, - types.DefaultVscTimeoutPeriod, - types.DefaultSlashMeterReplenishPeriod, - types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: math.NewInt(1000000)}, 600, 24), - nil, - nil, - nil, - nil, - nil, - ), - false, - }, - { - "invalid params, zero init timeout", - types.NewGenesisState( - 0, - nil, - []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id"}}, - nil, - nil, - nil, - nil, - types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, - time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, - ccv.DefaultCCVTimeoutPeriod, - 0, // 0 init timeout here - types.DefaultVscTimeoutPeriod, - types.DefaultSlashMeterReplenishPeriod, - types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24), - nil, - nil, - nil, - nil, - nil, - ), - false, - }, - { - "invalid params, zero vsc timeout", - types.NewGenesisState( - types.DefaultValsetUpdateID, - nil, - []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id"}}, - nil, - nil, - nil, - nil, - types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, - time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, - ccv.DefaultCCVTimeoutPeriod, - types.DefaultInitTimeoutPeriod, - 0, // 0 vsc timeout here types.DefaultSlashMeterReplenishPeriod, types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24), - nil, - nil, + sdk.Coin{Denom: "stake", Amount: math.NewInt(1000000)}, 600, 24, 180), nil, nil, nil, @@ -297,19 +184,13 @@ func TestValidateGenesisState(t *testing.T) { []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id"}}, nil, nil, - nil, - nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), types.DefaultTrustingPeriodFraction, ccv.DefaultCCVTimeoutPeriod, - types.DefaultInitTimeoutPeriod, - types.DefaultVscTimeoutPeriod, 0, // 0 slash meter replenish period here types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24), - nil, - nil, + sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24, 180), nil, nil, nil, @@ -324,19 +205,13 @@ func TestValidateGenesisState(t *testing.T) { []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id"}}, nil, nil, - nil, - nil, types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), types.DefaultTrustingPeriodFraction, ccv.DefaultCCVTimeoutPeriod, - types.DefaultInitTimeoutPeriod, - types.DefaultVscTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, "1.15", - sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24), - nil, - nil, + sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24, 180), nil, nil, nil, @@ -351,14 +226,10 @@ func TestValidateGenesisState(t *testing.T) { []types.ConsumerState{{ChainId: "", ChannelId: "channelid", ClientId: "client-id"}}, nil, nil, - nil, - nil, types.DefaultParams(), nil, nil, nil, - nil, - nil, ), false, }, @@ -370,14 +241,10 @@ func TestValidateGenesisState(t *testing.T) { []types.ConsumerState{{ChainId: "chainid", ChannelId: "ivnalidChannel{}", ClientId: "client-id"}}, nil, nil, - nil, - nil, types.DefaultParams(), nil, nil, nil, - nil, - nil, ), false, }, @@ -389,14 +256,10 @@ func TestValidateGenesisState(t *testing.T) { []types.ConsumerState{{ChainId: "chainid", ChannelId: "channel-0", ClientId: ""}}, nil, nil, - nil, - nil, types.DefaultParams(), nil, nil, nil, - nil, - nil, ), false, }, @@ -408,14 +271,10 @@ func TestValidateGenesisState(t *testing.T) { []types.ConsumerState{{ChainId: "chainid", ChannelId: "channel-0", ClientId: "abc", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid")}}, nil, nil, - nil, - nil, types.DefaultParams(), nil, nil, nil, - nil, - nil, ), false, }, @@ -430,14 +289,10 @@ func TestValidateGenesisState(t *testing.T) { }}, nil, nil, - nil, - nil, types.DefaultParams(), nil, nil, nil, - nil, - nil, ), false, }, @@ -453,14 +308,10 @@ func TestValidateGenesisState(t *testing.T) { }}, nil, nil, - nil, - nil, types.DefaultParams(), nil, nil, nil, - nil, - nil, ), false, }, @@ -476,14 +327,10 @@ func TestValidateGenesisState(t *testing.T) { }}, nil, nil, - nil, - nil, types.DefaultParams(), nil, nil, nil, - nil, - nil, ), false, }, @@ -503,174 +350,10 @@ func TestValidateGenesisState(t *testing.T) { }}, nil, nil, - nil, - nil, - types.DefaultParams(), - nil, - nil, - nil, - nil, - nil, - ), - false, - }, - { - "invalid consumer state UnbondingOpsIndex - zero vscID", - types.NewGenesisState( - types.DefaultValsetUpdateID, - nil, - []types.ConsumerState{ - { - ChainId: "chainid", - ChannelId: "channel-0", - ClientId: "client-id", - ConsumerGenesis: getInitialConsumerGenesis(t, "chainid"), - UnbondingOpsIndex: []types.VscUnbondingOps{{}}, - }, - }, - nil, - nil, - nil, - nil, - types.DefaultParams(), - nil, - nil, - nil, - nil, - nil, - ), - false, - }, - { - "invalid consumer state UnbondingOpsIndex - no IDs", - types.NewGenesisState( - types.DefaultValsetUpdateID, - nil, - []types.ConsumerState{ - { - ChainId: "chainid", - ChannelId: "channel-0", - ClientId: "client-id", - ConsumerGenesis: getInitialConsumerGenesis(t, "chainid"), - UnbondingOpsIndex: []types.VscUnbondingOps{{VscId: 1}}, - }, - }, - nil, - nil, - nil, - nil, - types.DefaultParams(), - nil, - nil, - nil, - nil, - nil, - ), - false, - }, - { - "invalid consumer state UnbondingOp - no matching UnbondingOpsIndex", - types.NewGenesisState( - types.DefaultValsetUpdateID, - nil, - []types.ConsumerState{ - { - ChainId: "chainid", - ChannelId: "channel-0", - ClientId: "client-id", - ConsumerGenesis: getInitialConsumerGenesis(t, "chainid"), - UnbondingOpsIndex: []types.VscUnbondingOps{ - { - VscId: 1, - }, - }, - }, - }, - []types.UnbondingOp{ - { - Id: 13, - UnbondingConsumerChains: []string{"chainid"}, - }, - }, - nil, - nil, - nil, - types.DefaultParams(), - nil, - nil, - nil, - nil, - nil, - ), - false, - }, - { - "invalid consumer state UnbondingOp - no matching UnbondingOpsIndex 2", - types.NewGenesisState( - types.DefaultValsetUpdateID, - nil, - []types.ConsumerState{ - { - ChainId: "chainid", - ChannelId: "channel-0", - ClientId: "client-id", - ConsumerGenesis: getInitialConsumerGenesis(t, "chainid"), - UnbondingOpsIndex: []types.VscUnbondingOps{ - { - VscId: 1, - UnbondingOpIds: []uint64{13}, - }, - }, - }, - { - ChainId: "chainid-2", - ChannelId: "channel-0", - ClientId: "client-id", - ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-2"), - UnbondingOpsIndex: []types.VscUnbondingOps{ - { - VscId: 1, - }, - }, - }, - }, - []types.UnbondingOp{ - { - Id: 13, - UnbondingConsumerChains: []string{"chainid", "chainid-2"}, - }, - }, - nil, - nil, - nil, - types.DefaultParams(), - nil, - nil, - nil, - nil, - nil, - ), - false, - }, - { - "invalid consumer state unbonding operation operation 2", - types.NewGenesisState( - types.DefaultValsetUpdateID, - nil, - []types.ConsumerState{{ - ChainId: "chainid", ChannelId: "channel-0", ClientId: "client-id", - UnbondingOpsIndex: []types.VscUnbondingOps{{VscId: 1}}, - }}, - nil, - nil, - nil, - nil, types.DefaultParams(), nil, nil, nil, - nil, - nil, ), false, }, @@ -682,13 +365,9 @@ func TestValidateGenesisState(t *testing.T) { []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-1")}}, nil, nil, - nil, - 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: math.NewInt(10000000)}, 600, 24), - nil, - nil, + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: math.NewInt(10000000)}, 600, 24, 180), nil, nil, nil, @@ -703,13 +382,9 @@ func TestValidateGenesisState(t *testing.T) { []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-1")}}, nil, nil, - nil, - 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: math.NewInt(-1000000)}, 600, 24), - nil, - nil, + types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(-1000000)}, 600, 24, 180), nil, nil, nil, diff --git a/x/ccv/provider/types/key_assignment.go b/x/ccv/provider/types/key_assignment.go index ee403e1e49..7149ebc6cc 100644 --- a/x/ccv/provider/types/key_assignment.go +++ b/x/ccv/provider/types/key_assignment.go @@ -67,7 +67,7 @@ func (c *ConsumerConsAddress) String() string { func KeyAssignmentValidateBasic( assignedKeys []ValidatorConsumerPubKey, byConsumerAddrs []ValidatorByConsumerAddr, - consumerAddrsToPrune []ConsumerAddrsToPrune, + consumerAddrsToPrune []ConsumerAddrsToPruneV2, ) error { for _, e := range assignedKeys { if strings.TrimSpace(e.ChainId) == "" { @@ -95,7 +95,6 @@ func KeyAssignmentValidateBasic( if strings.TrimSpace(e.ChainId) == "" { return errorsmod.Wrap(ccvtypes.ErrInvalidGenesis, "consumer chain id must not be blank") } - // Don't check e.vscid, it's an unsigned integer for _, a := range e.ConsumerAddrs.Addresses { if err := sdk.VerifyAddressFormat(a); err != nil { return errorsmod.Wrap(ccvtypes.ErrInvalidGenesis, fmt.Sprintf("invalid consumer address: %s", a)) diff --git a/x/ccv/provider/types/keys.go b/x/ccv/provider/types/keys.go index 47231c219f..bd9ccbd807 100644 --- a/x/ccv/provider/types/keys.go +++ b/x/ccv/provider/types/keys.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/binary" "fmt" + "sort" "time" sdk "github.com/cosmos/cosmos-sdk/types" @@ -31,233 +32,403 @@ const ( // This address receives rewards from consumer chains ConsumerRewardsPool = "consumer_rewards_pool" -) -// Iota generated keys/byte prefixes (as a byte), supports 256 possible values -const ( + // Names for the store keys. + // Used for storing the byte prefixes in the constant map. + // See getKeyPrefixes(). + + ParametersKeyName = "ParametersKey" + + PortKeyName = "PortKey" - // PortKey defines the key to store the port ID in store - PortByteKey byte = iota + DeprecatedMaturedUnbondingOpsKeyName = "DeprecatedMaturedUnbondingOpsKey" - // MaturedUnbondingOpsByteKey is the byte key that stores the list of all unbonding operations ids - // that have matured from a consumer chain perspective, - // i.e., no longer waiting on the unbonding period to elapse on any consumer chain - MaturedUnbondingOpsByteKey + ValidatorSetUpdateIdKeyName = "ValidatorSetUpdateIdKey" - // ValidatorSetUpdateIdByteKey is the byte key that stores the current validator set update id - ValidatorSetUpdateIdByteKey + SlashMeterKeyName = "SlashMeterKey" - // SlashMeterByteKey is the byte key for storing the slash meter - SlashMeterByteKey + SlashMeterReplenishTimeCandidateKeyName = "SlashMeterReplenishTimeCandidateKey" - // SlashMeterReplenishTimeCandidateByteKey is the byte key for storing the slash meter replenish time candidate - SlashMeterReplenishTimeCandidateByteKey + ChainToChannelKeyName = "ChainToChannelKey" - // ChainToChannelBytePrefix is the byte prefix for storing mapping - // from chainID to the channel ID that is used to send over validator set changes. - ChainToChannelBytePrefix + ChannelToChainKeyName = "ChannelToChainKey" - // ChannelToChainBytePrefix is the byte prefix for storing mapping - // from the CCV channel ID to the consumer chain ID. - ChannelToChainBytePrefix + ChainToClientKeyName = "ChainToClientKey" - // ChainToClientBytePrefix is the byte prefix for storing the client ID for a given consumer chainID. - ChainToClientBytePrefix + DeprecatedInitTimeoutTimestampKeyName = "DeprecatedInitTimeoutTimestampKey" - // InitTimeoutTimestampBytePrefix is the byte prefix for storing - // the init timeout timestamp for a given consumer chainID. - InitTimeoutTimestampBytePrefix + PendingCAPKeyName = "PendingCAPKey" - // PendingCAPBytePrefix is the byte prefix for storing pending consumer addition proposals before the spawn time occurs. - // The key includes the BigEndian timestamp to allow for efficient chronological iteration - PendingCAPBytePrefix + PendingCRPKeyName = "PendingCRPKey" - // PendingCRPBytePrefix is the byte prefix for storing pending consumer removal proposals before the stop time occurs. - // The key includes the BigEndian timestamp to allow for efficient chronological iteration - PendingCRPBytePrefix + DeprecatedUnbondingOpKeyName = "DeprecatedUnbondingOpKey" - // UnbondingOpBytePrefix is the byte prefix that stores a record of all the ids of consumer chains that - // need to unbond before a given unbonding operation can unbond on this chain. - UnbondingOpBytePrefix + DeprecatedUnbondingOpIndexKeyName = "DeprecatedUnbondingOpIndexKey" - // UnbondingOpIndexBytePrefix is byte prefix of the index for looking up which unbonding - // operations are waiting for a given consumer chain to unbond - UnbondingOpIndexBytePrefix + ValsetUpdateBlockHeightKeyName = "ValsetUpdateBlockHeightKey" - // ValsetUpdateBlockHeightBytePrefix is the byte prefix that will store the mapping from vscIDs to block heights - ValsetUpdateBlockHeightBytePrefix + ConsumerGenesisKeyName = "ConsumerGenesisKey" - // ConsumerGenesisBytePrefix stores consumer genesis state material (consensus state and client state) indexed by consumer chain id - ConsumerGenesisBytePrefix + SlashAcksKeyName = "SlashAcksKey" - // SlashAcksBytePrefix is the byte prefix that will store consensus address of consumer chain validators successfully slashed on the provider chain - SlashAcksBytePrefix + InitChainHeightKeyName = "InitChainHeightKey" - // InitChainHeightBytePrefix is the byte prefix that will store the mapping from a chain id to the corresponding block height on the provider - // this consumer chain was initialized - InitChainHeightBytePrefix + PendingVSCsKeyName = "PendingVSCsKey" - // PendingVSCsBytePrefix is the byte prefix that will store pending ValidatorSetChangePacket data - PendingVSCsBytePrefix + DeprecatedVscSendTimestampKeyName = "DeprecatedVscSendTimestampKey" - // VscSendTimestampBytePrefix is the byte prefix for storing - // the list of VSC sending timestamps for a given consumer chainID. - VscSendTimestampBytePrefix + ThrottledPacketDataSizeKeyName = "ThrottledPacketDataSizeKey" - // ThrottledPacketDataSizeBytePrefix is the byte prefix for storing the size of chain-specific throttled packet data queues - ThrottledPacketDataSizeBytePrefix + ThrottledPacketDataKeyName = "ThrottledPacketDataKey" - // ThrottledPacketDataBytePrefix is the byte prefix storing throttled packet data - ThrottledPacketDataBytePrefix + GlobalSlashEntryKeyName = "GlobalSlashEntryKey" - // GlobalSlashEntryBytePrefix is the byte prefix storing global slash queue entries - GlobalSlashEntryBytePrefix + ConsumerValidatorsKeyName = "ConsumerValidatorsKey" - // ConsumerValidatorsBytePrefix is the byte prefix that will store the validator assigned keys for every consumer chain - ConsumerValidatorsBytePrefix + ValidatorsByConsumerAddrKeyName = "ValidatorsByConsumerAddrKey" - // ValidatorsByConsumerAddrBytePrefix is the byte prefix that will store the mapping from validator addresses - // on consumer chains to validator addresses on the provider chain - ValidatorsByConsumerAddrBytePrefix + DeprecatedKeyAssignmentReplacementsKeyName = "DeprecatedKeyAssignmentReplacementsKey" - // KeyAssignmentReplacementsBytePrefix was the byte prefix used to store the key assignments that needed to be replaced in the current block - // NOTE: This prefix is deprecated, but left in place to avoid consumer state migrations - // [DEPRECATED] - KeyAssignmentReplacementsBytePrefix + DeprecatedConsumerAddrsToPruneKeyName = "DeprecatedConsumerAddrsToPruneKey" - // ConsumerAddrsToPruneBytePrefix is the byte prefix that will store the mapping from VSC ids - // to consumer validators addresses needed for pruning - ConsumerAddrsToPruneBytePrefix + SlashLogKeyName = "SlashLogKey" - // SlashLogBytePrefix is the byte prefix that will store the mapping from provider address to boolean - // denoting whether the provider address has committed any double signign infractions - SlashLogBytePrefix + ConsumerRewardDenomsKeyName = "ConsumerRewardDenomsKey" - // ConsumerRewardDenomsBytePrefix is the byte prefix that will store a list of consumer reward denoms - ConsumerRewardDenomsBytePrefix + DeprecatedVSCMaturedHandledThisBlockKeyName = "DeprecatedVSCMaturedHandledThisBlockKey" - // VSCMaturedHandledThisBlockBytePrefix is the byte prefix storing the number of vsc matured packets - // handled in the current block - VSCMaturedHandledThisBlockBytePrefix + EquivocationEvidenceMinHeightKeyName = "EquivocationEvidenceMinHeightKey" - // EquivocationEvidenceMinHeightBytePrefix is the byte prefix storing the mapping from consumer chain IDs - // to the minimum height of a valid consumer equivocation evidence - EquivocationEvidenceMinHeightBytePrefix + ProposedConsumerChainKeyName = "ProposedConsumerChainKey" - // ProposedConsumerChainByteKey is the byte prefix storing the consumer chainId in consumerAddition gov proposal submitted before voting finishes - ProposedConsumerChainByteKey + ConsumerValidatorKeyName = "ConsumerValidatorKey" - // 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 + OptedInKeyName = "OptedInKey" - // OptedInBytePrefix is the byte prefix for storing whether a validator is opted in to validate on a consumer chain - OptedInBytePrefix + TopNKeyName = "TopNKey" - // 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 + ValidatorsPowerCapKeyName = "ValidatorsPowerCapKey" - // 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 + ValidatorSetCapKeyName = "ValidatorSetCapKey" - // ValidatorSetCapPrefix is the byte prefix storing the mapping from a consumer chain to the validator-set cap value - // of this chain. - ValidatorSetCapPrefix + AllowlistKeyName = "AllowlistKey" - // AllowlistPrefix is the byte prefix storing the mapping from a consumer chain to the set of validators that are - // allowlisted. - AllowlistPrefix + DenylistKeyName = "DenylistKey" - // DenylistPrefix is the byte prefix storing the mapping from a consumer chain to the set of validators that are - // denylisted. - DenylistPrefix + ConsumerRewardsAllocationKeyName = "ConsumerRewardsAllocationKey" - // ConsumerRewardsAllocationBytePrefix is the byte prefix for storing for each consumer the ICS rewards - // allocated to the consumer rewards pool - ConsumerRewardsAllocationBytePrefix + ConsumerCommissionRateKeyName = "ConsumerCommissionRateKey" - // ConsumerCommissionRatePrefix is the byte prefix for storing the commission rate - // per validator per consumer chain - ConsumerCommissionRatePrefix + MinimumPowerInTopNKeyName = "MinimumPowerInTopNKey" - // MinimumPowerInTopNBytePrefix is the byte prefix for storing the - // minimum power required to be in the top N per consumer chain. - MinimumPowerInTopNBytePrefix + LastProviderConsensusValsKeyName = "LastProviderConsensusValsKey" - // NOTE: DO NOT ADD NEW BYTE PREFIXES HERE WITHOUT ADDING THEM TO getAllKeyPrefixes() IN keys_test.go + MinStakeKeyName = "MinStakeKey" + + AllowInactiveValidatorsKeyName = "AllowInactiveValidatorsKey" + + ConsumerAddrsToPruneV2KeyName = "ConsumerAddrsToPruneV2Key" ) +// getKeyPrefixes returns a constant map of all the byte prefixes for existing keys +func getKeyPrefixes() map[string]byte { + return map[string]byte{ + // ParametersKey is the is the key for storing provider's parameters. + // note that this was set to the max uint8 type value 0xFF in order to protect + // from using the ICS v5.0.0 provider module by mistake + ParametersKeyName: byte(0xFF), + + // PortKey defines the key to store the port ID in store + PortKeyName: 0, + + // MaturedUnbondingOpsKey is the key that stores the list of all unbonding operations ids + // that have matured from a consumer chain perspective, + // i.e., no longer waiting on the unbonding period to elapse on any consumer chain + // NOTE: This prefix is deprecated, but left in place to avoid state migrations + // [DEPRECATED] + DeprecatedMaturedUnbondingOpsKeyName: 1, + + // ValidatorSetUpdateIdKey is the key that stores the current validator set update id + ValidatorSetUpdateIdKeyName: 2, + + // SlashMeterKey is the key for storing the slash meter + SlashMeterKeyName: 3, + + // SlashMeterReplenishTimeCandidateKey is the key for storing the slash meter replenish time candidate + SlashMeterReplenishTimeCandidateKeyName: 4, + + // ChainToChannelKey is the key for storing mapping + // from chainID to the channel ID that is used to send over validator set changes. + ChainToChannelKeyName: 5, + + // ChannelToChainKey is the key for storing mapping + // from the CCV channel ID to the consumer chain ID. + ChannelToChainKeyName: 6, + + // ChainToClientKey is the key for storing the client ID for a given consumer chainID. + ChainToClientKeyName: 7, + + // InitTimeoutTimestampKey is the key for storing + // the init timeout timestamp for a given consumer chainID. + // NOTE: This prefix is deprecated, but left in place to avoid state migrations + // [DEPRECATED] + DeprecatedInitTimeoutTimestampKeyName: 8, + + // PendingCAPKey is the key for storing pending consumer addition proposals before the spawn time occurs. + // The key includes the BigEndian timestamp to allow for efficient chronological iteration + PendingCAPKeyName: 9, + + // PendingCRPKey is the key for storing pending consumer removal proposals before the stop time occurs. + // The key includes the BigEndian timestamp to allow for efficient chronological iteration + PendingCRPKeyName: 10, + + // UnbondingOpKey is the key that stores a record of all the ids of consumer chains that + // need to unbond before a given unbonding operation can unbond on this chain. + // NOTE: This prefix is deprecated, but left in place to avoid state migrations + // [DEPRECATED] + DeprecatedUnbondingOpKeyName: 11, + + // UnbondingOpIndexKey is key of the index for looking up which unbonding + // operations are waiting for a given consumer chain to unbond + // NOTE: This prefix is deprecated, but left in place to avoid state migrations + // [DEPRECATED] + DeprecatedUnbondingOpIndexKeyName: 12, + + // ValsetUpdateBlockHeightKey is the key for storing the mapping from vscIDs to block heights + ValsetUpdateBlockHeightKeyName: 13, + + // ConsumerGenesisKey stores consumer genesis state material (consensus state and client state) indexed by consumer chain id + ConsumerGenesisKeyName: 14, + + // SlashAcksKey is the key for storing consensus address of consumer chain validators successfully slashed on the provider chain + SlashAcksKeyName: 15, + + // InitChainHeightKey is the key for storing the mapping from a chain id to the corresponding block height on the provider + // this consumer chain was initialized + InitChainHeightKeyName: 16, + + // PendingVSCsKey is the key for storing pending ValidatorSetChangePacket data + PendingVSCsKeyName: 17, + + // VscSendTimestampKey is the key for storing + // the list of VSC sending timestamps for a given consumer chainID. + // NOTE: This prefix is deprecated, but left in place to avoid state migrations + // [DEPRECATED] + DeprecatedVscSendTimestampKeyName: 18, + + // ThrottledPacketDataSizeKey is the key for storing the size of chain-specific throttled packet data queues + ThrottledPacketDataSizeKeyName: 19, + + // ThrottledPacketDataKey is the key for storing throttled packet data + ThrottledPacketDataKeyName: 20, + + // GlobalSlashEntryKey is the key for storing global slash queue entries + GlobalSlashEntryKeyName: 21, + + // ConsumerValidatorsKey is the key for storing the validator assigned keys for every consumer chain + ConsumerValidatorsKeyName: 22, + + // ValidatorsByConsumerAddrKey is the key for storing the mapping from validator addresses + // on consumer chains to validator addresses on the provider chain + ValidatorsByConsumerAddrKeyName: 23, + + // DeprecatedKeyAssignmentReplacementsKey was the key used to store the key assignments that needed to be replaced in the current block + // NOTE: This prefix is deprecated, but left in place to avoid consumer state migrations + // [DEPRECATED] + DeprecatedKeyAssignmentReplacementsKeyName: 24, + + // ConsumerAddrsToPruneKey is the key for storing the mapping from VSC ids + // to consumer validators addresses needed for pruning + // NOTE: This prefix is deprecated, but left in place to avoid consumer state migrations + // [DEPRECATED] + DeprecatedConsumerAddrsToPruneKeyName: 25, + + // SlashLogKey is the key for storing the mapping from provider address to boolean + // denoting whether the provider address has committed any double signign infractions + SlashLogKeyName: 26, + + // ConsumerRewardDenomsKey is the key for storing a list of consumer reward denoms + ConsumerRewardDenomsKeyName: 27, + + // VSCMaturedHandledThisBlockKey is the key for storing the number of vsc matured packets + // handled in the current block + // NOTE: This prefix is deprecated, but left in place to avoid state migrations + // [DEPRECATED] + DeprecatedVSCMaturedHandledThisBlockKeyName: 28, + + // EquivocationEvidenceMinHeightKey is the key for storing the mapping from consumer chain IDs + // to the minimum height of a valid consumer equivocation evidence + EquivocationEvidenceMinHeightKeyName: 29, + + // ProposedConsumerChainKey is the key for storing the consumer chainId in consumerAddition gov proposal submitted before voting finishes + ProposedConsumerChainKeyName: 30, + + // ConsumerValidatorKey is the key for storing for each consumer chain all the consumer + // validators in this epoch that are validating the consumer chain + ConsumerValidatorKeyName: 31, + + // OptedInKey is the key for storing whether a validator is opted in to validate on a consumer chain + OptedInKeyName: 32, + + // TopNKey is the key for 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 + TopNKeyName: 33, + + // ValidatorsPowerCapKey is the key for 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. + ValidatorsPowerCapKeyName: 34, + + // ValidatorSetCapKey is the key for storing the mapping from a consumer chain to the validator-set cap value + // of this chain. + ValidatorSetCapKeyName: 35, + + // AllowlistKey is the key for storing the mapping from a consumer chain to the set of validators that are + // allowlisted. + AllowlistKeyName: 36, + + // DenylistKey is the key for storing the mapping from a consumer chain to the set of validators that are + // denylisted. + DenylistKeyName: 37, + + // ConsumerRewardsAllocationKey is the key for storing for each consumer the ICS rewards + // allocated to the consumer rewards pool + ConsumerRewardsAllocationKeyName: 38, + + // ConsumerCommissionRateKey is the key for storing the commission rate + // per validator per consumer chain + ConsumerCommissionRateKeyName: 39, + + // MinimumPowerInTopNKey is the key for storing the + // minimum power required to be in the top N per consumer chain. + MinimumPowerInTopNKeyName: 40, + + // ConsumerAddrsToPruneV2Key is the key for storing + // consumer validators addresses that need to be pruned. + ConsumerAddrsToPruneV2KeyName: 41, + + // LastProviderConsensusValsKey is the byte prefix for storing the last validator set + // sent to the consensus engine of the provider chain + LastProviderConsensusValsKeyName: 42, + + // MinStakeKey is the byte prefix for storing the mapping from consumer chains to the minimum stake required to be a validator on the consumer chain + // The minimum stake must be stored on the provider chain, not on the consumer chain itself, since it filters out + // validators from the VSCPackets that we send to the consumer chain. + MinStakeKeyName: 43, + + // AllowInactiveValidatorsKey is the byte prefix for storing the mapping from consumer chains to the boolean value + // that determines whether inactive validators can validate on that chain + AllowInactiveValidatorsKeyName: 44, + + // NOTE: DO NOT ADD NEW BYTE PREFIXES HERE WITHOUT ADDING THEM TO TestPreserveBytePrefix() IN keys_test.go + } +} + +// mustGetKeyPrefix returns the key prefix for a given key. +// It panics if there is not byte prefix for the index. +func mustGetKeyPrefix(key string) byte { + keyPrefixes := getKeyPrefixes() + if prefix, found := keyPrefixes[key]; !found { + panic(fmt.Sprintf("could not find key prefix for index %s", key)) + } else { + return prefix + } +} + +// GetAllKeyPrefixes returns all the key prefixes. +// Only used for testing +func GetAllKeyPrefixes() []byte { + prefixMap := getKeyPrefixes() + keys := make([]string, 0, len(prefixMap)) + for k := range prefixMap { + keys = append(keys, k) + } + sort.Strings(keys) + prefixList := make([]byte, 0, len(prefixMap)) + for _, k := range keys { + prefixList = append(prefixList, prefixMap[k]) + } + return prefixList +} + +// GetAllKeys returns the names of all the keys. +// Only used for testing +func GetAllKeyNames() []string { + prefixMap := getKeyPrefixes() + keys := make([]string, 0, len(prefixMap)) + for k := range prefixMap { + keys = append(keys, k) + } + sort.Strings(keys) + return keys +} + // // Fully defined key func section // -const ( - // ParametersKey is the is the single byte key for storing provider's parameters. - // note that this was set to the max uint8 type value 0xFF in order to protect - // from using the ICS v5.0.0 provider module by mistake - ParametersByteKey = byte(0xFF) -) - // ParametersKey returns the key for the parameters of the provider module in the store func ParametersKey() []byte { - return []byte{ParametersByteKey} + return []byte{mustGetKeyPrefix(ParametersKeyName)} } // PortKey returns the key to the port ID in the store func PortKey() []byte { - return []byte{PortByteKey} -} - -// MaturedUnbondingOpsKey returns the key for storing the list of matured unbonding operations. -func MaturedUnbondingOpsKey() []byte { - return []byte{MaturedUnbondingOpsByteKey} + return []byte{mustGetKeyPrefix(PortKeyName)} } // ValidatorSetUpdateIdKey is the key that stores the current validator set update id func ValidatorSetUpdateIdKey() []byte { - return []byte{ValidatorSetUpdateIdByteKey} + return []byte{mustGetKeyPrefix(ValidatorSetUpdateIdKeyName)} } // SlashMeterKey returns the key storing the slash meter func SlashMeterKey() []byte { - return []byte{SlashMeterByteKey} + return []byte{mustGetKeyPrefix(SlashMeterKeyName)} } // SlashMeterReplenishTimeCandidateKey returns the key storing the slash meter replenish time candidate func SlashMeterReplenishTimeCandidateKey() []byte { - return []byte{SlashMeterReplenishTimeCandidateByteKey} + return []byte{mustGetKeyPrefix(SlashMeterReplenishTimeCandidateKeyName)} } // ChainToChannelKey returns the key under which the CCV channel ID will be stored for the given consumer chain. func ChainToChannelKey(chainID string) []byte { - return append([]byte{ChainToChannelBytePrefix}, []byte(chainID)...) + return append([]byte{mustGetKeyPrefix(ChainToChannelKeyName)}, []byte(chainID)...) +} + +// ChannelToChainKeyPrefix returns the key prefix for storing the consumer chain IDs. +func ChannelToChainKeyPrefix() []byte { + return []byte{mustGetKeyPrefix(ChannelToChainKeyName)} } // ChannelToChainKey returns the key under which the consumer chain ID will be stored for the given channelID. func ChannelToChainKey(channelID string) []byte { - return append([]byte{ChannelToChainBytePrefix}, []byte(channelID)...) + return append(ChannelToChainKeyPrefix(), []byte(channelID)...) +} + +// ChainToClientKeyPrefix returns the key prefix for storing the clientID for the given chainID. +func ChainToClientKeyPrefix() []byte { + return []byte{mustGetKeyPrefix(ChainToClientKeyName)} } // ChainToClientKey returns the key under which the clientID for the given chainID is stored. func ChainToClientKey(chainID string) []byte { - return append([]byte{ChainToClientBytePrefix}, []byte(chainID)...) + return append(ChainToClientKeyPrefix(), []byte(chainID)...) } -// InitTimeoutTimestampKey returns the key under which the init timeout timestamp for the given chainID is stored. -func InitTimeoutTimestampKey(chainID string) []byte { - return append([]byte{InitTimeoutTimestampBytePrefix}, []byte(chainID)...) +// PendingCAPKeyPrefix returns the key prefix for storing a pending consumer addition proposal +func PendingCAPKeyPrefix() []byte { + return []byte{mustGetKeyPrefix(PendingCAPKeyName)} } // PendingCAPKey returns the key under which a pending consumer addition proposal is stored. -// The key has the following format: PendingCAPBytePrefix | timestamp.UnixNano() | chainID +// The key has the following format: PendingCAPKeyPrefix | timestamp.UnixNano() | chainID func PendingCAPKey(timestamp time.Time, chainID string) []byte { ts := uint64(timestamp.UTC().UnixNano()) return ccvtypes.AppendMany( // Append the prefix - []byte{PendingCAPBytePrefix}, + PendingCAPKeyPrefix(), // Append the time sdk.Uint64ToBigEndian(ts), // Append the chainId @@ -265,13 +436,18 @@ func PendingCAPKey(timestamp time.Time, chainID string) []byte { ) } +// PendingCRPKeyPrefix returns the key prefix for storing pending consumer removal proposals. +func PendingCRPKeyPrefix() []byte { + return []byte{mustGetKeyPrefix(PendingCRPKeyName)} +} + // PendingCRPKey returns the key under which pending consumer removal proposals are stored. -// The key has the following format: PendingCRPBytePrefix | timestamp.UnixNano() | chainID +// The key has the following format: PendingCRPKeyPrefix | timestamp.UnixNano() | chainID func PendingCRPKey(timestamp time.Time, chainID string) []byte { ts := uint64(timestamp.UTC().UnixNano()) return ccvtypes.AppendMany( // Append the prefix - []byte{PendingCRPBytePrefix}, + PendingCRPKeyPrefix(), // Append the time sdk.Uint64ToBigEndian(ts), // Append the chainId @@ -279,76 +455,53 @@ func PendingCRPKey(timestamp time.Time, chainID string) []byte { ) } -// UnbondingOpKey returns the key that stores a record of all the ids of consumer chains that -// need to unbond before a given unbonding operation can unbond on this chain. -func UnbondingOpKey(id uint64) []byte { - bz := make([]byte, 8) - binary.BigEndian.PutUint64(bz, id) - return append([]byte{UnbondingOpBytePrefix}, bz...) -} - -// UnbondingOpIndexKey returns an unbonding op index key -// Note: chainId is hashed to a fixed length sequence of bytes here to prevent -// injection attack between chainIDs. -func UnbondingOpIndexKey(chainID string, vscID uint64) []byte { - return ChainIdAndUintIdKey(UnbondingOpIndexBytePrefix, chainID, vscID) -} - -// ParseUnbondingOpIndexKey parses an unbonding op index key for VSC ID -// Removes the prefix + chainID from index key and returns only the key part. -func ParseUnbondingOpIndexKey(key []byte) (string, uint64, error) { - return ParseChainIdAndUintIdKey(UnbondingOpIndexBytePrefix, key) +// ValsetUpdateBlockHeightKeyPrefix returns the key prefix that storing the mapping from valset update ID to block height +func ValsetUpdateBlockHeightKeyPrefix() []byte { + return []byte{mustGetKeyPrefix(ValsetUpdateBlockHeightKeyName)} } // ValsetUpdateBlockHeightKey returns the key that storing the mapping from valset update ID to block height func ValsetUpdateBlockHeightKey(valsetUpdateId uint64) []byte { vuidBytes := make([]byte, 8) binary.BigEndian.PutUint64(vuidBytes, valsetUpdateId) - return append([]byte{ValsetUpdateBlockHeightBytePrefix}, vuidBytes...) + return append(ValsetUpdateBlockHeightKeyPrefix(), vuidBytes...) } // ConsumerGenesisKey returns the key corresponding to consumer genesis state material // (consensus state and client state) indexed by consumer chain id func ConsumerGenesisKey(chainID string) []byte { - return append([]byte{ConsumerGenesisBytePrefix}, []byte(chainID)...) + return append([]byte{mustGetKeyPrefix(ConsumerGenesisKeyName)}, []byte(chainID)...) } // SlashAcksKey returns the key under which slashing acks are stored for a given chain ID func SlashAcksKey(chainID string) []byte { - return append([]byte{SlashAcksBytePrefix}, []byte(chainID)...) + return append([]byte{mustGetKeyPrefix(SlashAcksKeyName)}, []byte(chainID)...) } // InitChainHeightKey returns the key under which the block height for a given chain ID is stored func InitChainHeightKey(chainID string) []byte { - return append([]byte{InitChainHeightBytePrefix}, []byte(chainID)...) + return append([]byte{mustGetKeyPrefix(InitChainHeightKeyName)}, []byte(chainID)...) } // PendingVSCsKey returns the key under which // pending ValidatorSetChangePacket data is stored for a given chain ID func PendingVSCsKey(chainID string) []byte { - return append([]byte{PendingVSCsBytePrefix}, []byte(chainID)...) -} - -// VscSendingTimestampKey returns the key under which the -// sending timestamp of the VSCPacket with vsc ID is stored -func VscSendingTimestampKey(chainID string, vscID uint64) []byte { - return ChainIdAndUintIdKey(VscSendTimestampBytePrefix, chainID, vscID) -} - -// ParseVscTimeoutTimestampKey returns chain ID and vsc ID -// for a VscSendingTimestampKey or an error if unparsable -func ParseVscSendingTimestampKey(bz []byte) (string, uint64, error) { - return ParseChainIdAndUintIdKey(VscSendTimestampBytePrefix, bz) + return append([]byte{mustGetKeyPrefix(PendingVSCsKeyName)}, []byte(chainID)...) } // ThrottledPacketDataSizeKey returns the key storing the size of the throttled packet data queue for a given chain ID func ThrottledPacketDataSizeKey(consumerChainID string) []byte { - return append([]byte{ThrottledPacketDataSizeBytePrefix}, []byte(consumerChainID)...) + return append([]byte{mustGetKeyPrefix(ThrottledPacketDataSizeKeyName)}, []byte(consumerChainID)...) +} + +// ThrottledPacketDataKeyPrefix returns the key prefix for storing the throttled packet data queue +func ThrottledPacketDataKeyPrefix() byte { + return mustGetKeyPrefix(ThrottledPacketDataKeyName) } -// ThrottledPacketDataKey returns the key storing the throttled packet data queue for a given chain ID and ibc seq num +// ThrottledPacketDataKey returns the key for storing the throttled packet data queue for a given chain ID and ibc seq num func ThrottledPacketDataKey(consumerChainID string, ibcSeqNum uint64) []byte { - return ChainIdAndUintIdKey(ThrottledPacketDataBytePrefix, consumerChainID, ibcSeqNum) + return ChainIdAndUintIdKey(ThrottledPacketDataKeyPrefix(), consumerChainID, ibcSeqNum) } // MustParseThrottledPacketDataKey parses a throttled packet data key or panics upon failure @@ -362,7 +515,12 @@ func MustParseThrottledPacketDataKey(key []byte) (string, uint64) { // ParseThrottledPacketDataKey parses a throttled packet data key func ParseThrottledPacketDataKey(key []byte) (chainId string, ibcSeqNum uint64, err error) { - return ParseChainIdAndUintIdKey(ThrottledPacketDataBytePrefix, key) + return ParseChainIdAndUintIdKey(ThrottledPacketDataKeyPrefix(), key) +} + +// GlobalSlashEntryKeyPrefix returns the key for storing a global slash queue entry. +func GlobalSlashEntryKeyPrefix() []byte { + return []byte{mustGetKeyPrefix(GlobalSlashEntryKeyName)} } // GlobalSlashEntryKey returns the key for storing a global slash queue entry. @@ -370,7 +528,7 @@ func GlobalSlashEntryKey(entry GlobalSlashEntry) []byte { recvTime := uint64(entry.RecvTime.UTC().UnixNano()) return ccvtypes.AppendMany( // Append byte prefix - []byte{GlobalSlashEntryBytePrefix}, + GlobalSlashEntryKeyPrefix(), // Append time bz sdk.Uint64ToBigEndian(recvTime), // Append ibc seq num @@ -386,7 +544,7 @@ func MustParseGlobalSlashEntryKey(bz []byte) ( recvTime time.Time, consumerChainID string, ibcSeqNum uint64, ) { // Prefix is in first byte - expectedPrefix := []byte{GlobalSlashEntryBytePrefix} + expectedPrefix := GlobalSlashEntryKeyPrefix() if prefix := bz[:1]; !bytes.Equal(prefix, expectedPrefix) { panic(fmt.Sprintf("invalid prefix; expected: %X, got: %X", expectedPrefix, prefix)) } @@ -404,38 +562,190 @@ func MustParseGlobalSlashEntryKey(bz []byte) ( return recvTime, chainID, ibcSeqNum } +// ConsumerValidatorsKey returns the key for storing the validator assigned keys for every consumer chain +func ConsumerValidatorsKeyPrefix() byte { + return mustGetKeyPrefix(ConsumerValidatorsKeyName) +} + // ConsumerValidatorsKey returns the key under which the // validator assigned keys for every consumer chain are stored func ConsumerValidatorsKey(chainID string, addr ProviderConsAddress) []byte { - return ChainIdAndConsAddrKey(ConsumerValidatorsBytePrefix, chainID, addr.ToSdkConsAddr()) + return ChainIdAndConsAddrKey(ConsumerValidatorsKeyPrefix(), chainID, addr.ToSdkConsAddr()) } -// ValidatorsByConsumerAddrKey returns the key under which the mapping from validator addresses -// on consumer chains to validator addresses on the provider chain is stored -func ValidatorsByConsumerAddrKey(chainID string, addr ConsumerConsAddress) []byte { - return ChainIdAndConsAddrKey(ValidatorsByConsumerAddrBytePrefix, chainID, addr.ToSdkConsAddr()) +// ValidatorsByConsumerAddrKeyPrefix returns the key prefix for storing the mapping from validator addresses +// on consumer chains to validator addresses on the provider chain +func ValidatorsByConsumerAddrKeyPrefix() byte { + return mustGetKeyPrefix(ValidatorsByConsumerAddrKeyName) } -// ConsumerAddrsToPruneKey returns the key under which the -// mapping from VSC ids to consumer validators addresses is stored -func ConsumerAddrsToPruneKey(chainID string, vscID uint64) []byte { - return ChainIdAndUintIdKey(ConsumerAddrsToPruneBytePrefix, chainID, vscID) +// ValidatorsByConsumerAddrKey returns the key for storing the mapping from validator addresses +// on consumer chains to validator addresses on the provider chain +func ValidatorsByConsumerAddrKey(chainID string, addr ConsumerConsAddress) []byte { + return ChainIdAndConsAddrKey(ValidatorsByConsumerAddrKeyPrefix(), chainID, addr.ToSdkConsAddr()) } // SlashLogKey returns the key to a validator's slash log func SlashLogKey(providerAddr ProviderConsAddress) []byte { - return append([]byte{SlashLogBytePrefix}, providerAddr.ToSdkConsAddr().Bytes()...) + return append([]byte{mustGetKeyPrefix(SlashLogKeyName)}, providerAddr.ToSdkConsAddr().Bytes()...) +} + +// ConsumerRewardDenomsKeyPrefix returns the key prefix for storing consumer reward denoms +func ConsumerRewardDenomsKeyPrefix() []byte { + return []byte{mustGetKeyPrefix(ConsumerRewardDenomsKeyName)} } -// ConsumerRewardDenomsKey returns the key under which consumer reward denoms are stored +// ConsumerRewardDenomsKey returns the key for storing consumer reward denoms func ConsumerRewardDenomsKey(denom string) []byte { - return append([]byte{ConsumerRewardDenomsBytePrefix}, []byte(denom)...) + return append(ConsumerRewardDenomsKeyPrefix(), []byte(denom)...) } // EquivocationEvidenceMinHeightKey returns the key storing the minimum height // of a valid consumer equivocation evidence for a given consumer chain ID func EquivocationEvidenceMinHeightKey(consumerChainID string) []byte { - return append([]byte{EquivocationEvidenceMinHeightBytePrefix}, []byte(consumerChainID)...) + return append([]byte{mustGetKeyPrefix(EquivocationEvidenceMinHeightKeyName)}, []byte(consumerChainID)...) +} + +// ProposedConsumerChainKeyPrefix returns the key prefix for storing proposed consumer chainId +// in consumerAddition gov proposal before voting finishes +func ProposedConsumerChainKeyPrefix() []byte { + return []byte{mustGetKeyPrefix(ProposedConsumerChainKeyName)} +} + +// ProposedConsumerChainKey returns the key of proposed consumer chainId in consumerAddition gov proposal before voting finishes, the stored key format is prefix|proposalID, value is chainID +func ProposedConsumerChainKey(proposalID uint64) []byte { + return ccvtypes.AppendMany( + ProposedConsumerChainKeyPrefix(), + sdk.Uint64ToBigEndian(proposalID), + ) +} + +// ParseProposedConsumerChainKey get the proposalID in the key +func ParseProposedConsumerChainKey(bz []byte) (uint64, error) { + expectedPrefix := ProposedConsumerChainKeyPrefix() + prefixL := len(expectedPrefix) + if prefix := bz[:prefixL]; !bytes.Equal(prefix, expectedPrefix) { + return 0, fmt.Errorf("invalid prefix; expected: %X, got: %X", expectedPrefix, prefix) + } + proposalID := sdk.BigEndianToUint64(bz[prefixL:]) + + return proposalID, nil +} + +// ConsumerValidatorKeyPrefix returns the key prefix for storing consumer validators +func ConsumerValidatorKeyPrefix() byte { + return mustGetKeyPrefix(ConsumerValidatorKeyName) +} + +// ConsumerValidatorKey returns the key for storing consumer validators +// for the given consumer chain `chainID` and validator with `providerAddr` +func ConsumerValidatorKey(chainID string, providerAddr []byte) []byte { + prefix := ChainIdWithLenKey(ConsumerValidatorKeyPrefix(), chainID) + 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(mustGetKeyPrefix(TopNKeyName), chainID) +} + +// ValidatorSetPowerKey returns the key of consumer chain `chainID` +func ValidatorsPowerCapKey(chainID string) []byte { + return ChainIdWithLenKey(mustGetKeyPrefix(ValidatorsPowerCapKeyName), chainID) +} + +// ValidatorSetCapKey returns the key of consumer chain `chainID` +func ValidatorSetCapKey(chainID string) []byte { + return ChainIdWithLenKey(mustGetKeyPrefix(ValidatorSetCapKeyName), chainID) +} + +// AllowlistKeyPrefix returns the key prefix for storing consumer chains allowlists +func AllowlistKeyPrefix() byte { + return mustGetKeyPrefix(AllowlistKeyName) +} + +// AllowlistKey returns the key for storing consumer chains allowlists +func AllowlistKey(chainID string, providerAddr ProviderConsAddress) []byte { + return append( + ChainIdWithLenKey(AllowlistKeyPrefix(), chainID), + providerAddr.ToSdkConsAddr().Bytes()..., + ) +} + +// DenylistKeyPrefix returns the key prefix for storing consumer chains denylists +func DenylistKeyPrefix() byte { + return mustGetKeyPrefix(DenylistKeyName) +} + +// DenylistKey returns the key for storing consumer chains denylists +func DenylistKey(chainID string, providerAddr ProviderConsAddress) []byte { + return append( + ChainIdWithLenKey(DenylistKeyPrefix(), chainID), + providerAddr.ToSdkConsAddr().Bytes()..., + ) +} + +// OptedInKeyPrefix returns the key prefix for storing whether a validator is opted in on a consumer chain. +func OptedInKeyPrefix() byte { + return mustGetKeyPrefix(OptedInKeyName) +} + +// 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(OptedInKeyPrefix(), 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{mustGetKeyPrefix(ConsumerRewardsAllocationKeyName)}, []byte(chainID)...) +} + +// ConsumerCommissionRateKeyPrefix returns the key prefix for storing the commission rate per validator per consumer chain. +func ConsumerCommissionRateKeyPrefix() byte { + return mustGetKeyPrefix(ConsumerCommissionRateKeyName) +} + +// ConsumerCommissionRateKey returns the key used to store the commission rate per validator per consumer chain. +func ConsumerCommissionRateKey(chainID string, providerAddr ProviderConsAddress) []byte { + return ChainIdAndConsAddrKey( + ConsumerCommissionRateKeyPrefix(), + chainID, + providerAddr.ToSdkConsAddr(), + ) +} + +func MinimumPowerInTopNKey(chainID string) []byte { + return ChainIdWithLenKey(mustGetKeyPrefix(MinimumPowerInTopNKeyName), chainID) +} + +// ConsumerAddrsToPruneV2KeyPrefix returns the key prefix for storing the consumer validators +// addresses that need to be pruned. These are stored as a +// (chainID, ts) -> (consumer_address1, consumer_address2, ...) mapping, where ts is the +// timestamp at which the consumer validators addresses can be pruned. +func ConsumerAddrsToPruneV2KeyPrefix() byte { + return mustGetKeyPrefix(ConsumerAddrsToPruneV2KeyName) +} + +// ConsumerAddrsToPruneV2Key returns the key for storing the consumer validators +// addresses that need to be pruned. +func ConsumerAddrsToPruneV2Key(chainID string, pruneTs time.Time) []byte { + return ChainIdAndTsKey(ConsumerAddrsToPruneV2KeyPrefix(), chainID, pruneTs) +} + +// LastProviderConsensusValsPrefix returns the key prefix for storing the last validator set sent to the consensus engine of the provider chain +func LastProviderConsensusValsPrefix() []byte { + return []byte{mustGetKeyPrefix(LastProviderConsensusValsKeyName)} +} + +// MinStakeKey returns the key used to store the minimum stake required to validate on consumer chain `chainID` +func MinStakeKey(chainID string) []byte { + return ChainIdWithLenKey(mustGetKeyPrefix(MinStakeKeyName), chainID) +} + +func AllowInactiveValidatorsKey(chainID string) []byte { + return ChainIdWithLenKey(mustGetKeyPrefix(AllowInactiveValidatorsKeyName), chainID) } // NOTE: DO NOT ADD FULLY DEFINED KEY FUNCTIONS WITHOUT ADDING THEM TO getAllFullyDefinedKeys() IN keys_test.go @@ -541,86 +851,6 @@ func ParseChainIdAndConsAddrKey(prefix byte, bz []byte) (string, sdk.ConsAddress return chainID, addr, nil } -func VSCMaturedHandledThisBlockKey() []byte { - return []byte{VSCMaturedHandledThisBlockBytePrefix} -} - -// ProposedConsumerChainKey returns the key of proposed consumer chainId in consumerAddition gov proposal before voting finishes, the stored key format is prefix|proposalID, value is chainID -func ProposedConsumerChainKey(proposalID uint64) []byte { - return ccvtypes.AppendMany( - []byte{ProposedConsumerChainByteKey}, - sdk.Uint64ToBigEndian(proposalID), - ) -} - -// ParseProposedConsumerChainKey get the proposalID in the key -func ParseProposedConsumerChainKey(prefix byte, bz []byte) (uint64, error) { - expectedPrefix := []byte{prefix} - prefixL := len(expectedPrefix) - if prefix := bz[:prefixL]; !bytes.Equal(prefix, expectedPrefix) { - return 0, fmt.Errorf("invalid prefix; expected: %X, got: %X", expectedPrefix, prefix) - } - proposalID := sdk.BigEndianToUint64(bz[prefixL:]) - - return proposalID, nil -} - -// ConsumerValidatorKey returns the key of consumer chain `chainID` and validator with `providerAddr` -func ConsumerValidatorKey(chainID string, providerAddr []byte) []byte { - prefix := ChainIdWithLenKey(ConsumerValidatorBytePrefix, chainID) - 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(), - ) -} - -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 3f6df5cb0a..800b6ee669 100644 --- a/x/ccv/provider/types/keys_test.go +++ b/x/ccv/provider/types/keys_test.go @@ -1,6 +1,7 @@ package types_test import ( + "strings" "testing" "time" @@ -9,12 +10,14 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" cryptoutil "github.com/cosmos/interchain-security/v5/testutil/crypto" + providerkeeper "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" ) // Tests that all singular keys, or prefixes to fully resolves keys are non duplicate byte values. func TestNoDuplicates(t *testing.T) { - prefixes := getAllKeyPrefixes() + prefixes := providertypes.GetAllKeyPrefixes() seen := []byte{} for _, prefix := range prefixes { @@ -23,51 +26,120 @@ func TestNoDuplicates(t *testing.T) { } } -// Returns all key prefixes to fully resolved keys, any of which should be a single, unique byte. -func getAllKeyPrefixes() []byte { - return []byte{ - providertypes.PortByteKey, - providertypes.MaturedUnbondingOpsByteKey, - providertypes.ValidatorSetUpdateIdByteKey, - providertypes.SlashMeterByteKey, - providertypes.SlashMeterReplenishTimeCandidateByteKey, - providertypes.ChainToChannelBytePrefix, - providertypes.ChannelToChainBytePrefix, - providertypes.ChainToClientBytePrefix, - providertypes.InitTimeoutTimestampBytePrefix, - providertypes.PendingCAPBytePrefix, - providertypes.PendingCRPBytePrefix, - providertypes.UnbondingOpBytePrefix, - providertypes.UnbondingOpIndexBytePrefix, - providertypes.ValsetUpdateBlockHeightBytePrefix, - providertypes.ConsumerGenesisBytePrefix, - providertypes.SlashAcksBytePrefix, - providertypes.InitChainHeightBytePrefix, - providertypes.PendingVSCsBytePrefix, - providertypes.VscSendTimestampBytePrefix, - providertypes.ThrottledPacketDataSizeBytePrefix, - providertypes.ThrottledPacketDataBytePrefix, - providertypes.GlobalSlashEntryBytePrefix, - providertypes.ConsumerValidatorsBytePrefix, - providertypes.ValidatorsByConsumerAddrBytePrefix, - providertypes.KeyAssignmentReplacementsBytePrefix, - providertypes.ConsumerAddrsToPruneBytePrefix, - providertypes.SlashLogBytePrefix, - providertypes.VSCMaturedHandledThisBlockBytePrefix, - providertypes.EquivocationEvidenceMinHeightBytePrefix, - providertypes.ProposedConsumerChainByteKey, - providertypes.ConsumerValidatorBytePrefix, - providertypes.OptedInBytePrefix, - providertypes.TopNBytePrefix, - providertypes.ConsumerRewardsAllocationBytePrefix, - providertypes.ConsumerCommissionRatePrefix, - providertypes.MinimumPowerInTopNBytePrefix, - providertypes.ParametersByteKey, - } +// Test that the value of all byte prefixes is preserved +func TestPreserveBytePrefix(t *testing.T) { + i := 0 + require.Equal(t, byte(0xFF), providertypes.ParametersKey()[0]) + i++ + require.Equal(t, byte(0), providertypes.PortKey()[0]) + i++ + // reserve 1 as deprecated + i++ + require.Equal(t, byte(2), providertypes.ValidatorSetUpdateIdKey()[0]) + i++ + require.Equal(t, byte(3), providertypes.SlashMeterKey()[0]) + i++ + require.Equal(t, byte(4), providertypes.SlashMeterReplenishTimeCandidateKey()[0]) + i++ + require.Equal(t, byte(5), providertypes.ChainToChannelKey("chainID")[0]) + i++ + require.Equal(t, byte(6), providertypes.ChannelToChainKeyPrefix()[0]) + i++ + require.Equal(t, byte(7), providertypes.ChainToClientKeyPrefix()[0]) + i++ + // reserve 8 as deprecated + i++ + require.Equal(t, byte(9), providertypes.PendingCAPKeyPrefix()[0]) + i++ + require.Equal(t, byte(10), providertypes.PendingCRPKeyPrefix()[0]) + i++ + // reserve 11 as deprecated + i++ + // reserve 12 as deprecated + i++ + require.Equal(t, byte(13), providertypes.ValsetUpdateBlockHeightKeyPrefix()[0]) + i++ + require.Equal(t, byte(14), providertypes.ConsumerGenesisKey("chainID")[0]) + i++ + require.Equal(t, byte(15), providertypes.SlashAcksKey("chainID")[0]) + i++ + require.Equal(t, byte(16), providertypes.InitChainHeightKey("chainID")[0]) + i++ + require.Equal(t, byte(17), providertypes.PendingVSCsKey("chainID")[0]) + i++ + // reserve 18 as deprecated + i++ + require.Equal(t, byte(19), providertypes.ThrottledPacketDataSizeKey("chainID")[0]) + i++ + require.Equal(t, byte(20), providertypes.ThrottledPacketDataKeyPrefix()) + i++ + require.Equal(t, byte(21), providertypes.GlobalSlashEntryKeyPrefix()[0]) + i++ + require.Equal(t, byte(22), providertypes.ConsumerValidatorsKeyPrefix()) + i++ + require.Equal(t, byte(23), providertypes.ValidatorsByConsumerAddrKeyPrefix()) + i++ + // reserve 24 as deprecated + i++ + // reserve 25 as deprecated + i++ + require.Equal(t, byte(26), providertypes.SlashLogKey(providertypes.NewProviderConsAddress([]byte{0x05}))[0]) + i++ + require.Equal(t, byte(27), providertypes.ConsumerRewardDenomsKeyPrefix()[0]) + i++ + // reserve 28 as deprecated + i++ + require.Equal(t, byte(29), providertypes.EquivocationEvidenceMinHeightKey("chainID")[0]) + i++ + require.Equal(t, byte(30), providertypes.ProposedConsumerChainKeyPrefix()[0]) + i++ + require.Equal(t, byte(31), providertypes.ConsumerValidatorKeyPrefix()) + i++ + require.Equal(t, byte(32), providertypes.OptedInKeyPrefix()) + i++ + require.Equal(t, byte(33), providertypes.TopNKey("chainID")[0]) + i++ + require.Equal(t, byte(34), providertypes.ValidatorsPowerCapKey("chainID")[0]) + i++ + require.Equal(t, byte(35), providertypes.ValidatorSetCapKey("chainID")[0]) + i++ + require.Equal(t, byte(36), providertypes.AllowlistKeyPrefix()) + i++ + require.Equal(t, byte(37), providertypes.DenylistKeyPrefix()) + i++ + require.Equal(t, byte(38), providertypes.ConsumerRewardsAllocationKey("chainID")[0]) + i++ + require.Equal(t, byte(39), providertypes.ConsumerCommissionRateKeyPrefix()) + i++ + require.Equal(t, byte(40), providertypes.MinimumPowerInTopNKey("chainID")[0]) + i++ + require.Equal(t, byte(41), providertypes.ConsumerAddrsToPruneV2KeyPrefix()) + i++ + require.Equal(t, byte(42), providertypes.LastProviderConsensusValsPrefix()[0]) + i++ + require.Equal(t, byte(43), providertypes.MinStakeKey("chainID")[0]) + i++ + require.Equal(t, byte(44), providertypes.AllowInactiveValidatorsKey("chainID")[0]) + i++ + + prefixes := providertypes.GetAllKeyPrefixes() + require.Equal(t, len(prefixes), i) } func TestNoPrefixOverlap(t *testing.T) { keys := getAllFullyDefinedKeys() + + // Make sure that we check all the fully defined keys. + // All non-deprecated keys should have such a function. + keyNames := providertypes.GetAllKeyNames() + nonDeprecatedKey := []string{} + for _, name := range keyNames { + if !strings.Contains(name, "Deprecated") { + nonDeprecatedKey = append(nonDeprecatedKey, name) + } + } + require.Equal(t, len(nonDeprecatedKey), len(keys)) + seenPrefixes := []byte{} for _, key := range keys { require.NotContains(t, seenPrefixes, key[0], "Duplicate key prefix: %v", key[0]) @@ -79,34 +151,44 @@ func TestNoPrefixOverlap(t *testing.T) { // Note we only care about checking prefixes here, so parameters into the key functions are arbitrary. func getAllFullyDefinedKeys() [][]byte { return [][]byte{ + providertypes.ParametersKey(), providertypes.PortKey(), - providertypes.MaturedUnbondingOpsKey(), providertypes.ValidatorSetUpdateIdKey(), providertypes.SlashMeterKey(), providertypes.SlashMeterReplenishTimeCandidateKey(), providertypes.ChainToChannelKey("chainID"), providertypes.ChannelToChainKey("channelID"), providertypes.ChainToClientKey("chainID"), - providertypes.InitTimeoutTimestampKey("chainID"), providertypes.PendingCAPKey(time.Time{}, "chainID"), providertypes.PendingCRPKey(time.Time{}, "chainID"), - providertypes.UnbondingOpKey(7), - providertypes.UnbondingOpIndexKey("chainID", 7), providertypes.ValsetUpdateBlockHeightKey(7), providertypes.ConsumerGenesisKey("chainID"), providertypes.SlashAcksKey("chainID"), providertypes.InitChainHeightKey("chainID"), providertypes.PendingVSCsKey("chainID"), - providertypes.VscSendingTimestampKey("chainID", 8), providertypes.ThrottledPacketDataSizeKey("chainID"), providertypes.ThrottledPacketDataKey("chainID", 88), providertypes.GlobalSlashEntryKey(providertypes.GlobalSlashEntry{}), providertypes.ConsumerValidatorsKey("chainID", providertypes.NewProviderConsAddress([]byte{0x05})), providertypes.ValidatorsByConsumerAddrKey("chainID", providertypes.NewConsumerConsAddress([]byte{0x05})), - providertypes.ConsumerAddrsToPruneKey("chainID", 88), providertypes.SlashLogKey(providertypes.NewProviderConsAddress([]byte{0x05})), - providertypes.VSCMaturedHandledThisBlockKey(), + providertypes.ConsumerRewardDenomsKey("uatom"), providertypes.EquivocationEvidenceMinHeightKey("chainID"), + providertypes.ProposedConsumerChainKey(1), + providertypes.ConsumerValidatorKey("chainID", providertypes.NewProviderConsAddress([]byte{0x05}).Address.Bytes()), + providertypes.TopNKey("chainID"), + providertypes.ValidatorsPowerCapKey("chainID"), + providertypes.ValidatorSetCapKey("chainID"), + providertypes.AllowlistKey("chainID", providertypes.NewProviderConsAddress([]byte{0x05})), + providertypes.DenylistKey("chainID", providertypes.NewProviderConsAddress([]byte{0x05})), + providertypes.OptedInKey("chainID", providertypes.NewProviderConsAddress([]byte{0x05})), + providertypes.ConsumerRewardsAllocationKey("chainID"), + providertypes.ConsumerCommissionRateKey("chainID", providertypes.NewProviderConsAddress([]byte{0x05})), + providertypes.MinimumPowerInTopNKey("chainID"), + providertypes.ConsumerAddrsToPruneV2Key("chainID", time.Time{}), + providertypes.MinStakeKey("chainID"), + providertypes.AllowInactiveValidatorsKey("chainID"), + providerkeeper.GetValidatorKey(types.LastProviderConsensusValsPrefix(), providertypes.NewProviderConsAddress([]byte{0x05})), } } @@ -255,24 +337,12 @@ func TestKeysWithPrefixAndId(t *testing.T) { providertypes.ChainToChannelKey, providertypes.ChannelToChainKey, providertypes.ChainToClientKey, - providertypes.InitTimeoutTimestampKey, providertypes.ConsumerGenesisKey, providertypes.SlashAcksKey, providertypes.InitChainHeightKey, providertypes.PendingVSCsKey, } - expectedBytePrefixes := []byte{ - providertypes.ChainToChannelBytePrefix, - providertypes.ChannelToChainBytePrefix, - providertypes.ChainToClientBytePrefix, - providertypes.InitTimeoutTimestampBytePrefix, - providertypes.ConsumerGenesisBytePrefix, - providertypes.SlashAcksBytePrefix, - providertypes.InitChainHeightBytePrefix, - providertypes.PendingVSCsBytePrefix, - } - tests := []struct { stringID string }{ @@ -282,9 +352,8 @@ func TestKeysWithPrefixAndId(t *testing.T) { } for _, test := range tests { - for funcIdx, function := range funcs { + for _, function := range funcs { key := function(test.stringID) - require.Equal(t, expectedBytePrefixes[funcIdx], key[0]) require.Equal(t, []byte(test.stringID), key[1:]) } } @@ -292,15 +361,9 @@ func TestKeysWithPrefixAndId(t *testing.T) { func TestKeysWithUint64Payload(t *testing.T) { funcs := []func(uint64) []byte{ - providertypes.UnbondingOpKey, providertypes.ValsetUpdateBlockHeightKey, } - expectedBytePrefixes := []byte{ - providertypes.UnbondingOpBytePrefix, - providertypes.ValsetUpdateBlockHeightBytePrefix, - } - tests := []struct { integer uint64 }{ @@ -311,9 +374,8 @@ func TestKeysWithUint64Payload(t *testing.T) { } for _, test := range tests { - for funcIdx, function := range funcs { + for _, function := range funcs { key := function(test.integer) - require.Equal(t, expectedBytePrefixes[funcIdx], key[0]) require.Equal(t, sdk.Uint64ToBigEndian(test.integer), key[1:]) } } @@ -331,8 +393,7 @@ func TestParseProposedConsumerChainKey(t *testing.T) { for _, test := range tests { key := providertypes.ProposedConsumerChainKey(test.proposalID) - pID, err := providertypes.ParseProposedConsumerChainKey( - providertypes.ProposedConsumerChainByteKey, key) + pID, err := providertypes.ParseProposedConsumerChainKey(key) require.NoError(t, err) require.Equal(t, pID, test.proposalID) } diff --git a/x/ccv/provider/types/legacy_proposal.go b/x/ccv/provider/types/legacy_proposal.go index 83b23156f9..3124cc3d87 100644 --- a/x/ccv/provider/types/legacy_proposal.go +++ b/x/ccv/provider/types/legacy_proposal.go @@ -58,6 +58,8 @@ func NewConsumerAdditionProposal(title, description, chainID string, validatorSetCap uint32, allowlist []string, denylist []string, + minStake uint64, + allowInactiveVals bool, ) govv1beta1.Content { return &ConsumerAdditionProposal{ Title: title, @@ -79,6 +81,8 @@ func NewConsumerAdditionProposal(title, description, chainID string, ValidatorSetCap: validatorSetCap, Allowlist: allowlist, Denylist: denylist, + MinStake: minStake, + AllowInactiveVals: allowInactiveVals, } } @@ -243,6 +247,8 @@ func NewConsumerModificationProposal(title, description, chainID string, validatorSetCap uint32, allowlist []string, denylist []string, + minStake uint64, + allowInactiveVals bool, ) govv1beta1.Content { return &ConsumerModificationProposal{ Title: title, @@ -253,6 +259,8 @@ func NewConsumerModificationProposal(title, description, chainID string, ValidatorSetCap: validatorSetCap, Allowlist: allowlist, Denylist: denylist, + MinStake: minStake, + AllowInactiveVals: allowInactiveVals, } } diff --git a/x/ccv/provider/types/legacy_proposal_test.go b/x/ccv/provider/types/legacy_proposal_test.go index 729a4318ca..02fdb5245e 100644 --- a/x/ccv/provider/types/legacy_proposal_test.go +++ b/x/ccv/provider/types/legacy_proposal_test.go @@ -41,6 +41,8 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), true, }, @@ -59,6 +61,8 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), true, }, @@ -77,6 +81,8 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), false, }, @@ -95,6 +101,8 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), false, }, @@ -133,6 +141,8 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), false, }, @@ -150,7 +160,10 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, 0, nil, - nil), + nil, + 0, + false, + ), false, }, { @@ -168,6 +181,8 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), false, }, @@ -186,6 +201,8 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), false, }, @@ -204,6 +221,8 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), false, }, @@ -222,6 +241,8 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), false, }, @@ -240,6 +261,8 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), false, }, @@ -258,6 +281,8 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), false, }, @@ -276,6 +301,8 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), false, }, @@ -294,6 +321,8 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), false, }, @@ -312,6 +341,8 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), false, }, @@ -330,6 +361,8 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), false, }, @@ -348,6 +381,8 @@ func TestConsumerAdditionProposalValidateBasic(t *testing.T) { 101, []string{"addr1"}, []string{"addr2", "addr3"}, + 0, + false, ), true, }, @@ -377,7 +412,10 @@ func TestMarshalConsumerAdditionProposal(t *testing.T) { 0, 0, nil, - nil) + nil, + 0, + false, + ) cccp, ok := content.(*types.ConsumerAdditionProposal) require.True(t, ok) @@ -424,7 +462,10 @@ func TestConsumerAdditionProposalString(t *testing.T) { 0, 0, []string{}, - []string{}) + []string{}, + 0, + false, + ) expect := fmt.Sprintf(`CreateConsumerChain Proposal Title: title @@ -515,6 +556,8 @@ func TestConsumerModificationProposalValidateBasic(t *testing.T) { 34, []string{"addr1"}, nil, + 0, + false, ), true, }, @@ -526,6 +569,8 @@ func TestConsumerModificationProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), false, }, @@ -537,6 +582,8 @@ func TestConsumerModificationProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), false, }, @@ -548,6 +595,8 @@ func TestConsumerModificationProposalValidateBasic(t *testing.T) { 0, nil, nil, + 0, + false, ), false, }, @@ -559,6 +608,8 @@ func TestConsumerModificationProposalValidateBasic(t *testing.T) { 101, []string{"addr1"}, []string{"addr2", "addr3"}, + 0, + false, ), true, }, diff --git a/x/ccv/provider/types/msg.go b/x/ccv/provider/types/msg.go index f74d8b9d78..c47c62e0a0 100644 --- a/x/ccv/provider/types/msg.go +++ b/x/ccv/provider/types/msg.go @@ -293,7 +293,6 @@ func (msg *MsgConsumerAddition) ValidateBasic() error { } func (msg *MsgConsumerRemoval) ValidateBasic() error { - if strings.TrimSpace(msg.ChainId) == "" { return errorsmod.Wrap(ErrInvalidConsumerRemovalProp, "consumer chain id must not be blank") } @@ -424,11 +423,12 @@ func (msg MsgOptOut) ValidateBasic() error { } // NewMsgSetConsumerCommissionRate creates a new MsgSetConsumerCommissionRate msg instance. -func NewMsgSetConsumerCommissionRate(chainID string, commission math.LegacyDec, providerValidatorAddress sdk.ValAddress) *MsgSetConsumerCommissionRate { +func NewMsgSetConsumerCommissionRate(chainID string, commission math.LegacyDec, providerValidatorAddress sdk.ValAddress, signer string) *MsgSetConsumerCommissionRate { return &MsgSetConsumerCommissionRate{ ChainId: chainID, Rate: commission, ProviderAddr: providerValidatorAddress.String(), + Signer: signer, } } diff --git a/x/ccv/provider/types/params.go b/x/ccv/provider/types/params.go index 81d69cd7eb..3fddd002a1 100644 --- a/x/ccv/provider/types/params.go +++ b/x/ccv/provider/types/params.go @@ -24,12 +24,6 @@ const ( // as UnbondingPeriod * TrustingPeriodFraction DefaultTrustingPeriodFraction = "0.66" - // DefaultInitTimeoutPeriod defines the init timeout period - DefaultInitTimeoutPeriod = 7 * 24 * time.Hour - - // DefaultVscTimeoutPeriod defines the VSC timeout period - DefaultVscTimeoutPeriod = 5 * 7 * 24 * time.Hour - // DefaultSlashMeterReplenishPeriod defines the default period for which the slash gas meter is replenished DefaultSlashMeterReplenishPeriod = time.Hour @@ -53,6 +47,10 @@ const ( // 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) + + // DefaultMaxProviderConsensusValidators is the default maximum number of validators that will + // be passed on from the staking module to the consensus engine on the provider. + DefaultMaxProviderConsensusValidators = 180 ) // Reflection based keys for params subspace @@ -62,13 +60,12 @@ const ( 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") KeyNumberOfEpochsToStartReceivingRewards = []byte("NumberOfEpochsToStartReceivingRewards") + KeyMaxProviderConsensusValidators = []byte("MaxProviderConsensusValidators") ) // ParamKeyTable returns a key table with the necessary registered provider params @@ -81,25 +78,23 @@ func NewParams( cs *ibctmtypes.ClientState, trustingPeriodFraction string, ccvTimeoutPeriod time.Duration, - initTimeoutPeriod time.Duration, - vscTimeoutPeriod time.Duration, slashMeterReplenishPeriod time.Duration, slashMeterReplenishFraction string, consumerRewardDenomRegistrationFee sdk.Coin, blocksPerEpoch int64, numberOfEpochsToStartReceivingRewards int64, + maxProviderConsensusValidators int64, ) Params { return Params{ TemplateClient: cs, TrustingPeriodFraction: trustingPeriodFraction, CcvTimeoutPeriod: ccvTimeoutPeriod, - InitTimeoutPeriod: initTimeoutPeriod, - VscTimeoutPeriod: vscTimeoutPeriod, SlashMeterReplenishPeriod: slashMeterReplenishPeriod, SlashMeterReplenishFraction: slashMeterReplenishFraction, ConsumerRewardDenomRegistrationFee: consumerRewardDenomRegistrationFee, BlocksPerEpoch: blocksPerEpoch, NumberOfEpochsToStartReceivingRewards: numberOfEpochsToStartReceivingRewards, + MaxProviderConsensusValidators: maxProviderConsensusValidators, } } @@ -120,8 +115,6 @@ func DefaultParams() Params { ), DefaultTrustingPeriodFraction, ccvtypes.DefaultCCVTimeoutPeriod, - DefaultInitTimeoutPeriod, - DefaultVscTimeoutPeriod, DefaultSlashMeterReplenishPeriod, DefaultSlashMeterReplenishFraction, // Defining this inline because it's not possible to define a constant of type sdk.Coin. @@ -132,6 +125,7 @@ func DefaultParams() Params { }, DefaultBlocksPerEpoch, DefaultNumberOfEpochsToStartReceivingRewards, + DefaultMaxProviderConsensusValidators, ) } @@ -149,12 +143,6 @@ func (p Params) Validate() error { if err := ccvtypes.ValidateDuration(p.CcvTimeoutPeriod); err != nil { return fmt.Errorf("ccv timeout period is invalid: %s", err) } - if err := ccvtypes.ValidateDuration(p.InitTimeoutPeriod); err != nil { - return fmt.Errorf("init timeout period is invalid: %s", err) - } - if err := ccvtypes.ValidateDuration(p.VscTimeoutPeriod); err != nil { - return fmt.Errorf("vsc timeout period is invalid: %s", err) - } if err := ccvtypes.ValidateDuration(p.SlashMeterReplenishPeriod); err != nil { return fmt.Errorf("slash meter replenish period is invalid: %s", err) } @@ -170,6 +158,10 @@ func (p Params) Validate() error { if err := ccvtypes.ValidatePositiveInt64(p.NumberOfEpochsToStartReceivingRewards); err != nil { return fmt.Errorf("number of epochs to start receiving rewards is invalid: %s", err) } + + if err := ccvtypes.ValidatePositiveInt64(p.MaxProviderConsensusValidators); err != nil { + return fmt.Errorf("max provider consensus validators is invalid: %s", err) + } return nil } @@ -179,13 +171,12 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { paramtypes.NewParamSetPair(KeyTemplateClient, p.TemplateClient, ValidateTemplateClient), paramtypes.NewParamSetPair(KeyTrustingPeriodFraction, p.TrustingPeriodFraction, ccvtypes.ValidateStringFraction), paramtypes.NewParamSetPair(ccvtypes.KeyCCVTimeoutPeriod, p.CcvTimeoutPeriod, ccvtypes.ValidateDuration), - paramtypes.NewParamSetPair(KeyInitTimeoutPeriod, p.InitTimeoutPeriod, ccvtypes.ValidateDuration), - paramtypes.NewParamSetPair(KeyVscTimeoutPeriod, p.VscTimeoutPeriod, ccvtypes.ValidateDuration), paramtypes.NewParamSetPair(KeySlashMeterReplenishPeriod, p.SlashMeterReplenishPeriod, ccvtypes.ValidateDuration), 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), + paramtypes.NewParamSetPair(KeyMaxProviderConsensusValidators, p.MaxProviderConsensusValidators, ccvtypes.ValidatePositiveInt64), } } diff --git a/x/ccv/provider/types/params_test.go b/x/ccv/provider/types/params_test.go index 8ff8da58f1..12bfee81a0 100644 --- a/x/ccv/provider/types/params_test.go +++ b/x/ccv/provider/types/params_test.go @@ -25,43 +25,37 @@ 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: math.NewInt(10000000)}, 1000, 24), true}, + "0.33", time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24, 180), 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: math.NewInt(10000000)}, 1000, 24), false}, + "0.33", time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24, 180), 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: math.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: math.NewInt(10000000)}, 1000, 24), false}, + "0.33", time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24, 180), false}, + {"nil client", types.NewParams(nil, "0.33", time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24, 180), 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: math.NewInt(10000000)}, 1000, 24), true}, + "0.00", time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24, 180), 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: math.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: math.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: math.NewInt(10000000)}, 1000, 24), false}, + "0.33", 0, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24, 180), 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: math.NewInt(10000000)}, 1000, 24), false}, + "0.33", time.Hour, 0, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24, 180), 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: math.NewInt(10000000)}, 1000, 24), false}, + "0.33", time.Hour, time.Hour, "1.5", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24, 180), 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: math.NewInt(10000000)}, 1000, 24), false}, + "0.33", time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: math.NewInt(10000000)}, 1000, 24, 180), 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: math.NewInt(-10000000)}, 1000, 24), false}, + "0.33", time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(-10000000)}, 1000, 24, 180), 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: math.NewInt(10000000)}, 1000, 0), false}, + "0.33", time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 0, 180), false}, } for _, tc := range testCases { diff --git a/x/ccv/provider/types/provider.pb.go b/x/ccv/provider/types/provider.pb.go index e602595797..7c30781221 100644 --- a/x/ccv/provider/types/provider.pb.go +++ b/x/ccv/provider/types/provider.pb.go @@ -113,6 +113,10 @@ type ConsumerAdditionProposal struct { 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"` + // Corresponds to the minimal amount of (provider chain) stake required to validate on the consumer chain. + MinStake uint64 `protobuf:"varint,20,opt,name=min_stake,json=minStake,proto3" json:"min_stake,omitempty"` + // Corresponds to whether inactive validators are allowed to validate the consumer chain. + AllowInactiveVals bool `protobuf:"varint,21,opt,name=allow_inactive_vals,json=allowInactiveVals,proto3" json:"allow_inactive_vals,omitempty"` } func (m *ConsumerAdditionProposal) Reset() { *m = ConsumerAdditionProposal{} } @@ -253,6 +257,10 @@ type ConsumerModificationProposal struct { 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"` + // Corresponds to the minimal amount of (provider chain) stake required to validate on the consumer chain. + MinStake uint64 `protobuf:"varint,9,opt,name=min_stake,json=minStake,proto3" json:"min_stake,omitempty"` + // Corresponds to whether inactive validators are allowed to validate the consumer chain. + AllowInactiveVals bool `protobuf:"varint,10,opt,name=allow_inactive_vals,json=allowInactiveVals,proto3" json:"allow_inactive_vals,omitempty"` } func (m *ConsumerModificationProposal) Reset() { *m = ConsumerModificationProposal{} } @@ -344,6 +352,20 @@ func (m *ConsumerModificationProposal) GetDenylist() []string { return nil } +func (m *ConsumerModificationProposal) GetMinStake() uint64 { + if m != nil { + return m.MinStake + } + return 0 +} + +func (m *ConsumerModificationProposal) GetAllowInactiveVals() bool { + if m != nil { + return m.AllowInactiveVals + } + return false +} + // EquivocationProposal is a governance proposal on the provider chain to // punish a validator for equivocation on a consumer chain. // @@ -579,14 +601,6 @@ type Params struct { TrustingPeriodFraction string `protobuf:"bytes,2,opt,name=trusting_period_fraction,json=trustingPeriodFraction,proto3" json:"trusting_period_fraction,omitempty"` // Sent IBC packets will timeout after this duration CcvTimeoutPeriod time.Duration `protobuf:"bytes,3,opt,name=ccv_timeout_period,json=ccvTimeoutPeriod,proto3,stdduration" json:"ccv_timeout_period"` - // The channel initialization (IBC channel opening handshake) will timeout - // after this duration - InitTimeoutPeriod time.Duration `protobuf:"bytes,4,opt,name=init_timeout_period,json=initTimeoutPeriod,proto3,stdduration" json:"init_timeout_period"` - // The VSC packets sent by the provider will timeout after this duration. - // Note that unlike ccv_timeout_period which is an IBC param, - // the vsc_timeout_period is a provider-side param that enables the provider - // to timeout VSC packets even when a consumer chain is not live. - VscTimeoutPeriod time.Duration `protobuf:"bytes,5,opt,name=vsc_timeout_period,json=vscTimeoutPeriod,proto3,stdduration" json:"vsc_timeout_period"` // The period for which the slash meter is replenished SlashMeterReplenishPeriod time.Duration `protobuf:"bytes,6,opt,name=slash_meter_replenish_period,json=slashMeterReplenishPeriod,proto3,stdduration" json:"slash_meter_replenish_period"` // The fraction of total voting power that is replenished to the slash meter @@ -599,6 +613,9 @@ type Params struct { 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"` + // The maximal number of validators that will be passed + // to the consensus engine on the provider. + MaxProviderConsensusValidators int64 `protobuf:"varint,12,opt,name=max_provider_consensus_validators,json=maxProviderConsensusValidators,proto3" json:"max_provider_consensus_validators,omitempty"` } func (m *Params) Reset() { *m = Params{} } @@ -655,20 +672,6 @@ func (m *Params) GetCcvTimeoutPeriod() time.Duration { return 0 } -func (m *Params) GetInitTimeoutPeriod() time.Duration { - if m != nil { - return m.InitTimeoutPeriod - } - return 0 -} - -func (m *Params) GetVscTimeoutPeriod() time.Duration { - if m != nil { - return m.VscTimeoutPeriod - } - return 0 -} - func (m *Params) GetSlashMeterReplenishPeriod() time.Duration { if m != nil { return m.SlashMeterReplenishPeriod @@ -704,6 +707,13 @@ func (m *Params) GetNumberOfEpochsToStartReceivingRewards() int64 { return 0 } +func (m *Params) GetMaxProviderConsensusValidators() int64 { + if m != nil { + return m.MaxProviderConsensusValidators + } + return 0 +} + // SlashAcks contains cons addresses of consumer chain validators // successfully slashed on the provider chain. type SlashAcks struct { @@ -942,219 +952,6 @@ func (m *ChannelToChain) GetChainId() string { return "" } -// VscUnbondingOps contains the IDs of unbonding operations that are waiting for -// at least one VSCMaturedPacket with vscID from a consumer chain -type VscUnbondingOps struct { - VscId uint64 `protobuf:"varint,1,opt,name=vsc_id,json=vscId,proto3" json:"vsc_id,omitempty"` - UnbondingOpIds []uint64 `protobuf:"varint,2,rep,packed,name=unbonding_op_ids,json=unbondingOpIds,proto3" json:"unbonding_op_ids,omitempty"` -} - -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{12} -} -func (m *VscUnbondingOps) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *VscUnbondingOps) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_VscUnbondingOps.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 *VscUnbondingOps) XXX_Merge(src proto.Message) { - xxx_messageInfo_VscUnbondingOps.Merge(m, src) -} -func (m *VscUnbondingOps) XXX_Size() int { - return m.Size() -} -func (m *VscUnbondingOps) XXX_DiscardUnknown() { - xxx_messageInfo_VscUnbondingOps.DiscardUnknown(m) -} - -var xxx_messageInfo_VscUnbondingOps proto.InternalMessageInfo - -func (m *VscUnbondingOps) GetVscId() uint64 { - if m != nil { - return m.VscId - } - return 0 -} - -func (m *VscUnbondingOps) GetUnbondingOpIds() []uint64 { - if m != nil { - return m.UnbondingOpIds - } - return nil -} - -// UnbondingOp contains the ids of consumer chains that need to unbond before -// the unbonding operation with the given ID can unbond -type UnbondingOp struct { - Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - // consumer chains that are still unbonding - UnbondingConsumerChains []string `protobuf:"bytes,2,rep,name=unbonding_consumer_chains,json=unbondingConsumerChains,proto3" json:"unbonding_consumer_chains,omitempty"` -} - -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{13} -} -func (m *UnbondingOp) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *UnbondingOp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_UnbondingOp.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 *UnbondingOp) XXX_Merge(src proto.Message) { - xxx_messageInfo_UnbondingOp.Merge(m, src) -} -func (m *UnbondingOp) XXX_Size() int { - return m.Size() -} -func (m *UnbondingOp) XXX_DiscardUnknown() { - xxx_messageInfo_UnbondingOp.DiscardUnknown(m) -} - -var xxx_messageInfo_UnbondingOp proto.InternalMessageInfo - -func (m *UnbondingOp) GetId() uint64 { - if m != nil { - return m.Id - } - return 0 -} - -func (m *UnbondingOp) GetUnbondingConsumerChains() []string { - if m != nil { - return m.UnbondingConsumerChains - } - return nil -} - -type InitTimeoutTimestamp struct { - ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` - Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` -} - -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{14} -} -func (m *InitTimeoutTimestamp) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *InitTimeoutTimestamp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_InitTimeoutTimestamp.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 *InitTimeoutTimestamp) XXX_Merge(src proto.Message) { - xxx_messageInfo_InitTimeoutTimestamp.Merge(m, src) -} -func (m *InitTimeoutTimestamp) XXX_Size() int { - return m.Size() -} -func (m *InitTimeoutTimestamp) XXX_DiscardUnknown() { - xxx_messageInfo_InitTimeoutTimestamp.DiscardUnknown(m) -} - -var xxx_messageInfo_InitTimeoutTimestamp proto.InternalMessageInfo - -func (m *InitTimeoutTimestamp) GetChainId() string { - if m != nil { - return m.ChainId - } - return "" -} - -func (m *InitTimeoutTimestamp) GetTimestamp() uint64 { - if m != nil { - return m.Timestamp - } - return 0 -} - -type VscSendTimestamp struct { - VscId uint64 `protobuf:"varint,1,opt,name=vsc_id,json=vscId,proto3" json:"vsc_id,omitempty"` - Timestamp time.Time `protobuf:"bytes,2,opt,name=timestamp,proto3,stdtime" json:"timestamp"` -} - -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{15} -} -func (m *VscSendTimestamp) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *VscSendTimestamp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_VscSendTimestamp.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 *VscSendTimestamp) XXX_Merge(src proto.Message) { - xxx_messageInfo_VscSendTimestamp.Merge(m, src) -} -func (m *VscSendTimestamp) XXX_Size() int { - return m.Size() -} -func (m *VscSendTimestamp) XXX_DiscardUnknown() { - xxx_messageInfo_VscSendTimestamp.DiscardUnknown(m) -} - -var xxx_messageInfo_VscSendTimestamp proto.InternalMessageInfo - -func (m *VscSendTimestamp) GetVscId() uint64 { - if m != nil { - return m.VscId - } - return 0 -} - -func (m *VscSendTimestamp) GetTimestamp() time.Time { - if m != nil { - return m.Timestamp - } - return time.Time{} -} - // ValidatorSetChangePackets is a pb list of ccv.ValidatorSetChangePacketData. type ValidatorSetChangePackets struct { List []types3.ValidatorSetChangePacketData `protobuf:"bytes,1,rep,name=list,proto3" json:"list"` @@ -1164,7 +961,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{16} + return fileDescriptor_f22ec409a72b7b72, []int{12} } func (m *ValidatorSetChangePackets) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1200,105 +997,6 @@ func (m *ValidatorSetChangePackets) GetList() []types3.ValidatorSetChangePacketD return nil } -// MaturedUnbondingOps defines a list of ids corresponding to ids of matured -// unbonding operations. -type MaturedUnbondingOps struct { - Ids []uint64 `protobuf:"varint,1,rep,packed,name=ids,proto3" json:"ids,omitempty"` -} - -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{17} -} -func (m *MaturedUnbondingOps) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *MaturedUnbondingOps) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_MaturedUnbondingOps.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 *MaturedUnbondingOps) XXX_Merge(src proto.Message) { - xxx_messageInfo_MaturedUnbondingOps.Merge(m, src) -} -func (m *MaturedUnbondingOps) XXX_Size() int { - return m.Size() -} -func (m *MaturedUnbondingOps) XXX_DiscardUnknown() { - xxx_messageInfo_MaturedUnbondingOps.DiscardUnknown(m) -} - -var xxx_messageInfo_MaturedUnbondingOps proto.InternalMessageInfo - -func (m *MaturedUnbondingOps) GetIds() []uint64 { - if m != nil { - return m.Ids - } - return nil -} - -// ExportedVscSendTimestamps is VscSendTimestamp with chainID info for exporting to genesis -type ExportedVscSendTimestamp struct { - ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` - VscSendTimestamps []VscSendTimestamp `protobuf:"bytes,2,rep,name=vsc_send_timestamps,json=vscSendTimestamps,proto3" json:"vsc_send_timestamps"` -} - -func (m *ExportedVscSendTimestamp) Reset() { *m = ExportedVscSendTimestamp{} } -func (m *ExportedVscSendTimestamp) String() string { return proto.CompactTextString(m) } -func (*ExportedVscSendTimestamp) ProtoMessage() {} -func (*ExportedVscSendTimestamp) Descriptor() ([]byte, []int) { - return fileDescriptor_f22ec409a72b7b72, []int{18} -} -func (m *ExportedVscSendTimestamp) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ExportedVscSendTimestamp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ExportedVscSendTimestamp.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 *ExportedVscSendTimestamp) XXX_Merge(src proto.Message) { - xxx_messageInfo_ExportedVscSendTimestamp.Merge(m, src) -} -func (m *ExportedVscSendTimestamp) XXX_Size() int { - return m.Size() -} -func (m *ExportedVscSendTimestamp) XXX_DiscardUnknown() { - xxx_messageInfo_ExportedVscSendTimestamp.DiscardUnknown(m) -} - -var xxx_messageInfo_ExportedVscSendTimestamp proto.InternalMessageInfo - -func (m *ExportedVscSendTimestamp) GetChainId() string { - if m != nil { - return m.ChainId - } - return "" -} - -func (m *ExportedVscSendTimestamp) GetVscSendTimestamps() []VscSendTimestamp { - if m != nil { - return m.VscSendTimestamps - } - return nil -} - type KeyAssignmentReplacement struct { ProviderAddr []byte `protobuf:"bytes,1,opt,name=provider_addr,json=providerAddr,proto3" json:"provider_addr,omitempty"` PrevCKey *crypto.PublicKey `protobuf:"bytes,2,opt,name=prev_c_key,json=prevCKey,proto3" json:"prev_c_key,omitempty"` @@ -1309,7 +1007,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{19} + return fileDescriptor_f22ec409a72b7b72, []int{13} } func (m *KeyAssignmentReplacement) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1372,7 +1070,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{20} + return fileDescriptor_f22ec409a72b7b72, []int{14} } func (m *ValidatorConsumerPubKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1435,7 +1133,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{21} + return fileDescriptor_f22ec409a72b7b72, []int{15} } func (m *ValidatorByConsumerAddr) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1485,26 +1183,26 @@ func (m *ValidatorByConsumerAddr) GetProviderAddr() []byte { return nil } -// Used to serialize the ConsumerAddrsToPrune index from key assignment -// ConsumerAddrsToPrune: (chainID, vscID uint64) -> consumerAddrs AddressList -type ConsumerAddrsToPrune struct { +// Used to serialize the ConsumerAddrsToPruneV2 index from key assignment +// ConsumerAddrsToPruneV2: (chainID, pruneTs time.Time) -> consumerAddrs AddressList +type ConsumerAddrsToPruneV2 struct { ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` - VscId uint64 `protobuf:"varint,2,opt,name=vsc_id,json=vscId,proto3" json:"vsc_id,omitempty"` + PruneTs time.Time `protobuf:"bytes,2,opt,name=prune_ts,json=pruneTs,proto3,stdtime" json:"prune_ts"` ConsumerAddrs *AddressList `protobuf:"bytes,3,opt,name=consumer_addrs,json=consumerAddrs,proto3" json:"consumer_addrs,omitempty"` } -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{22} +func (m *ConsumerAddrsToPruneV2) Reset() { *m = ConsumerAddrsToPruneV2{} } +func (m *ConsumerAddrsToPruneV2) String() string { return proto.CompactTextString(m) } +func (*ConsumerAddrsToPruneV2) ProtoMessage() {} +func (*ConsumerAddrsToPruneV2) Descriptor() ([]byte, []int) { + return fileDescriptor_f22ec409a72b7b72, []int{16} } -func (m *ConsumerAddrsToPrune) XXX_Unmarshal(b []byte) error { +func (m *ConsumerAddrsToPruneV2) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *ConsumerAddrsToPrune) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *ConsumerAddrsToPruneV2) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_ConsumerAddrsToPrune.Marshal(b, m, deterministic) + return xxx_messageInfo_ConsumerAddrsToPruneV2.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -1514,48 +1212,51 @@ func (m *ConsumerAddrsToPrune) XXX_Marshal(b []byte, deterministic bool) ([]byte return b[:n], nil } } -func (m *ConsumerAddrsToPrune) XXX_Merge(src proto.Message) { - xxx_messageInfo_ConsumerAddrsToPrune.Merge(m, src) +func (m *ConsumerAddrsToPruneV2) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConsumerAddrsToPruneV2.Merge(m, src) } -func (m *ConsumerAddrsToPrune) XXX_Size() int { +func (m *ConsumerAddrsToPruneV2) XXX_Size() int { return m.Size() } -func (m *ConsumerAddrsToPrune) XXX_DiscardUnknown() { - xxx_messageInfo_ConsumerAddrsToPrune.DiscardUnknown(m) +func (m *ConsumerAddrsToPruneV2) XXX_DiscardUnknown() { + xxx_messageInfo_ConsumerAddrsToPruneV2.DiscardUnknown(m) } -var xxx_messageInfo_ConsumerAddrsToPrune proto.InternalMessageInfo +var xxx_messageInfo_ConsumerAddrsToPruneV2 proto.InternalMessageInfo -func (m *ConsumerAddrsToPrune) GetChainId() string { +func (m *ConsumerAddrsToPruneV2) GetChainId() string { if m != nil { return m.ChainId } return "" } -func (m *ConsumerAddrsToPrune) GetVscId() uint64 { +func (m *ConsumerAddrsToPruneV2) GetPruneTs() time.Time { if m != nil { - return m.VscId + return m.PruneTs } - return 0 + return time.Time{} } -func (m *ConsumerAddrsToPrune) GetConsumerAddrs() *AddressList { +func (m *ConsumerAddrsToPruneV2) GetConsumerAddrs() *AddressList { if m != nil { return m.ConsumerAddrs } return nil } -// ConsumerValidator is used to facilitate epoch-based transitions. It contains relevant info for -// a validator that is expected to validate on a consumer chain during an epoch. -type ConsumerValidator struct { +// ConsensusValidator is used to express a validator that +// should be validating on a chain. +// It contains relevant info for +// a validator that is expected to validate on +// either the provider or a consumer chain. +type ConsensusValidator struct { // validator's consensus address on the provider chain ProviderConsAddr []byte `protobuf:"bytes,1,opt,name=provider_cons_addr,json=providerConsAddr,proto3" json:"provider_cons_addr,omitempty"` // voting power the validator has during this epoch 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"` + PublicKey *crypto.PublicKey `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"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 @@ -1563,18 +1264,18 @@ type ConsumerValidator struct { JoinHeight int64 `protobuf:"varint,4,opt,name=join_height,json=joinHeight,proto3" json:"join_height,omitempty"` } -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{23} +func (m *ConsensusValidator) Reset() { *m = ConsensusValidator{} } +func (m *ConsensusValidator) String() string { return proto.CompactTextString(m) } +func (*ConsensusValidator) ProtoMessage() {} +func (*ConsensusValidator) Descriptor() ([]byte, []int) { + return fileDescriptor_f22ec409a72b7b72, []int{17} } -func (m *ConsumerValidator) XXX_Unmarshal(b []byte) error { +func (m *ConsensusValidator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *ConsumerValidator) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *ConsensusValidator) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_ConsumerValidator.Marshal(b, m, deterministic) + return xxx_messageInfo_ConsensusValidator.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -1584,40 +1285,40 @@ func (m *ConsumerValidator) XXX_Marshal(b []byte, deterministic bool) ([]byte, e return b[:n], nil } } -func (m *ConsumerValidator) XXX_Merge(src proto.Message) { - xxx_messageInfo_ConsumerValidator.Merge(m, src) +func (m *ConsensusValidator) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConsensusValidator.Merge(m, src) } -func (m *ConsumerValidator) XXX_Size() int { +func (m *ConsensusValidator) XXX_Size() int { return m.Size() } -func (m *ConsumerValidator) XXX_DiscardUnknown() { - xxx_messageInfo_ConsumerValidator.DiscardUnknown(m) +func (m *ConsensusValidator) XXX_DiscardUnknown() { + xxx_messageInfo_ConsensusValidator.DiscardUnknown(m) } -var xxx_messageInfo_ConsumerValidator proto.InternalMessageInfo +var xxx_messageInfo_ConsensusValidator proto.InternalMessageInfo -func (m *ConsumerValidator) GetProviderConsAddr() []byte { +func (m *ConsensusValidator) GetProviderConsAddr() []byte { if m != nil { return m.ProviderConsAddr } return nil } -func (m *ConsumerValidator) GetPower() int64 { +func (m *ConsensusValidator) GetPower() int64 { if m != nil { return m.Power } return 0 } -func (m *ConsumerValidator) GetConsumerPublicKey() *crypto.PublicKey { +func (m *ConsensusValidator) GetPublicKey() *crypto.PublicKey { if m != nil { - return m.ConsumerPublicKey + return m.PublicKey } return nil } -func (m *ConsumerValidator) GetJoinHeight() int64 { +func (m *ConsensusValidator) GetJoinHeight() int64 { if m != nil { return m.JoinHeight } @@ -1635,7 +1336,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{24} + return fileDescriptor_f22ec409a72b7b72, []int{18} } func (m *ConsumerRewardsAllocation) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1684,18 +1385,12 @@ func init() { proto.RegisterType((*ConsumerRemovalProposals)(nil), "interchain_security.ccv.provider.v1.ConsumerRemovalProposals") proto.RegisterType((*AddressList)(nil), "interchain_security.ccv.provider.v1.AddressList") proto.RegisterType((*ChannelToChain)(nil), "interchain_security.ccv.provider.v1.ChannelToChain") - proto.RegisterType((*VscUnbondingOps)(nil), "interchain_security.ccv.provider.v1.VscUnbondingOps") - proto.RegisterType((*UnbondingOp)(nil), "interchain_security.ccv.provider.v1.UnbondingOp") - proto.RegisterType((*InitTimeoutTimestamp)(nil), "interchain_security.ccv.provider.v1.InitTimeoutTimestamp") - proto.RegisterType((*VscSendTimestamp)(nil), "interchain_security.ccv.provider.v1.VscSendTimestamp") proto.RegisterType((*ValidatorSetChangePackets)(nil), "interchain_security.ccv.provider.v1.ValidatorSetChangePackets") - proto.RegisterType((*MaturedUnbondingOps)(nil), "interchain_security.ccv.provider.v1.MaturedUnbondingOps") - proto.RegisterType((*ExportedVscSendTimestamp)(nil), "interchain_security.ccv.provider.v1.ExportedVscSendTimestamp") proto.RegisterType((*KeyAssignmentReplacement)(nil), "interchain_security.ccv.provider.v1.KeyAssignmentReplacement") proto.RegisterType((*ValidatorConsumerPubKey)(nil), "interchain_security.ccv.provider.v1.ValidatorConsumerPubKey") 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((*ConsumerAddrsToPruneV2)(nil), "interchain_security.ccv.provider.v1.ConsumerAddrsToPruneV2") + proto.RegisterType((*ConsensusValidator)(nil), "interchain_security.ccv.provider.v1.ConsensusValidator") proto.RegisterType((*ConsumerRewardsAllocation)(nil), "interchain_security.ccv.provider.v1.ConsumerRewardsAllocation") } @@ -1704,136 +1399,129 @@ func init() { } var fileDescriptor_f22ec409a72b7b72 = []byte{ - // 2052 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xbb, 0x6f, 0x1c, 0xc7, - 0x19, 0xe7, 0xf2, 0x8e, 0x8f, 0xfb, 0x8e, 0xcf, 0x21, 0x6d, 0x2d, 0x19, 0xe6, 0x48, 0xad, 0x23, - 0x85, 0xb1, 0xa2, 0x3b, 0x53, 0x86, 0x01, 0x41, 0x88, 0x61, 0xf0, 0x21, 0x5b, 0x0f, 0x4b, 0xa2, - 0x97, 0x8c, 0x84, 0x38, 0xc5, 0x62, 0x6e, 0x76, 0x78, 0x37, 0xe1, 0xde, 0xce, 0x6a, 0x66, 0x6e, - 0xe9, 0x6b, 0x52, 0xa7, 0x09, 0xe0, 0x74, 0x46, 0x8a, 0xc4, 0x49, 0x15, 0xb8, 0x4a, 0x91, 0x3e, - 0x40, 0x8a, 0xc0, 0x48, 0x13, 0x97, 0xa9, 0xec, 0x40, 0x2a, 0x52, 0xe4, 0x9f, 0x08, 0x66, 0xf6, - 0x79, 0x7c, 0x48, 0x27, 0x38, 0x6e, 0xc8, 0xdb, 0xef, 0xf1, 0xfb, 0xbe, 0x99, 0xef, 0xb9, 0x0b, - 0x37, 0x58, 0xa8, 0xa8, 0x20, 0x5d, 0xcc, 0x42, 0x4f, 0x52, 0xd2, 0x17, 0x4c, 0x0d, 0x5a, 0x84, - 0xc4, 0xad, 0x48, 0xf0, 0x98, 0xf9, 0x54, 0xb4, 0xe2, 0xad, 0xfc, 0x77, 0x33, 0x12, 0x5c, 0x71, - 0xf4, 0xc6, 0x39, 0x3a, 0x4d, 0x42, 0xe2, 0x66, 0x2e, 0x17, 0x6f, 0xad, 0x5e, 0xb9, 0x08, 0x38, - 0xde, 0x6a, 0x9d, 0x30, 0x41, 0x13, 0xac, 0xd5, 0xe5, 0x0e, 0xef, 0x70, 0xf3, 0xb3, 0xa5, 0x7f, - 0xa5, 0xd4, 0xf5, 0x0e, 0xe7, 0x9d, 0x80, 0xb6, 0xcc, 0x53, 0xbb, 0x7f, 0xd4, 0x52, 0xac, 0x47, - 0xa5, 0xc2, 0xbd, 0x28, 0x15, 0x68, 0x9c, 0x16, 0xf0, 0xfb, 0x02, 0x2b, 0xc6, 0xc3, 0x0c, 0x80, - 0xb5, 0x49, 0x8b, 0x70, 0x41, 0x5b, 0x24, 0x60, 0x34, 0x54, 0xda, 0x6a, 0xf2, 0x2b, 0x15, 0x68, - 0x69, 0x81, 0x80, 0x75, 0xba, 0x2a, 0x21, 0xcb, 0x96, 0xa2, 0xa1, 0x4f, 0x45, 0x8f, 0x25, 0xc2, - 0xc5, 0x53, 0xaa, 0xb0, 0x56, 0xe2, 0x13, 0x31, 0x88, 0x14, 0x6f, 0x1d, 0xd3, 0x81, 0x4c, 0xb9, - 0x57, 0x09, 0x97, 0x3d, 0x2e, 0x5b, 0x54, 0x9f, 0x3f, 0x24, 0xb4, 0x15, 0x6f, 0xb5, 0xa9, 0xc2, - 0x5b, 0x39, 0x21, 0xf3, 0x3b, 0x95, 0x6b, 0x63, 0x59, 0xc8, 0x10, 0xce, 0x32, 0xbf, 0x57, 0x12, - 0xbe, 0x97, 0xdc, 0x48, 0xf2, 0x90, 0xb2, 0x16, 0x71, 0x8f, 0x85, 0xbc, 0x65, 0xfe, 0x26, 0x24, - 0xe7, 0x8b, 0x69, 0xb0, 0x77, 0x79, 0x28, 0xfb, 0x3d, 0x2a, 0xb6, 0x7d, 0x9f, 0xe9, 0x0b, 0xd8, - 0x17, 0x3c, 0xe2, 0x12, 0x07, 0x68, 0x19, 0x26, 0x14, 0x53, 0x01, 0xb5, 0xad, 0x0d, 0x6b, 0xb3, - 0xe6, 0x26, 0x0f, 0x68, 0x03, 0xea, 0x3e, 0x95, 0x44, 0xb0, 0x48, 0x0b, 0xdb, 0xe3, 0x86, 0x57, - 0x26, 0xa1, 0x15, 0x98, 0x4e, 0xa2, 0xc6, 0x7c, 0xbb, 0x62, 0xd8, 0x53, 0xe6, 0xf9, 0xae, 0x8f, - 0x3e, 0x80, 0x39, 0x16, 0x32, 0xc5, 0x70, 0xe0, 0x75, 0xa9, 0xbe, 0x3b, 0xbb, 0xba, 0x61, 0x6d, - 0xd6, 0x6f, 0xac, 0x36, 0x59, 0x9b, 0x34, 0xf5, 0x75, 0x37, 0xd3, 0x4b, 0x8e, 0xb7, 0x9a, 0x77, - 0x8c, 0xc4, 0x4e, 0xf5, 0xcb, 0xaf, 0xd7, 0xc7, 0xdc, 0xd9, 0x54, 0x2f, 0x21, 0xa2, 0xcb, 0x30, - 0xd3, 0xa1, 0x21, 0x95, 0x4c, 0x7a, 0x5d, 0x2c, 0xbb, 0xf6, 0xc4, 0x86, 0xb5, 0x39, 0xe3, 0xd6, - 0x53, 0xda, 0x1d, 0x2c, 0xbb, 0x68, 0x1d, 0xea, 0x6d, 0x16, 0x62, 0x31, 0x48, 0x24, 0x26, 0x8d, - 0x04, 0x24, 0x24, 0x23, 0xb0, 0x0b, 0x20, 0x23, 0x7c, 0x12, 0x7a, 0x3a, 0x37, 0xec, 0xa9, 0xd4, - 0x91, 0x24, 0x2f, 0x9a, 0x59, 0x5e, 0x34, 0x0f, 0xb3, 0xc4, 0xd9, 0x99, 0xd6, 0x8e, 0x7c, 0xfa, - 0xcd, 0xba, 0xe5, 0xd6, 0x8c, 0x9e, 0xe6, 0xa0, 0x87, 0xb0, 0xd0, 0x0f, 0xdb, 0x3c, 0xf4, 0x59, - 0xd8, 0xf1, 0x22, 0x2a, 0x18, 0xf7, 0xed, 0x69, 0x03, 0xb5, 0x72, 0x06, 0x6a, 0x2f, 0x4d, 0xb1, - 0x04, 0xe9, 0x33, 0x8d, 0x34, 0x9f, 0x2b, 0xef, 0x1b, 0x5d, 0xf4, 0x11, 0x20, 0x42, 0x62, 0xe3, - 0x12, 0xef, 0xab, 0x0c, 0xb1, 0x36, 0x3a, 0xe2, 0x02, 0x21, 0xf1, 0x61, 0xa2, 0x9d, 0x42, 0xfe, - 0x1c, 0x2e, 0x29, 0x81, 0x43, 0x79, 0x44, 0xc5, 0x69, 0x5c, 0x18, 0x1d, 0xf7, 0xb5, 0x0c, 0x63, - 0x18, 0xfc, 0x0e, 0x6c, 0x90, 0x34, 0x81, 0x3c, 0x41, 0x7d, 0x26, 0x95, 0x60, 0xed, 0xbe, 0xd6, - 0xf5, 0x8e, 0x04, 0x26, 0x26, 0x47, 0xea, 0x26, 0x09, 0x1a, 0x99, 0x9c, 0x3b, 0x24, 0xf6, 0x7e, - 0x2a, 0x85, 0x1e, 0xc1, 0x0f, 0xda, 0x01, 0x27, 0xc7, 0x52, 0x3b, 0xe7, 0x0d, 0x21, 0x19, 0xd3, - 0x3d, 0x26, 0xa5, 0x46, 0x9b, 0xd9, 0xb0, 0x36, 0x2b, 0xee, 0xe5, 0x44, 0x76, 0x9f, 0x8a, 0xbd, - 0x92, 0xe4, 0x61, 0x49, 0x10, 0x5d, 0x07, 0xd4, 0x65, 0x52, 0x71, 0xc1, 0x08, 0x0e, 0x3c, 0x1a, - 0x2a, 0xc1, 0xa8, 0xb4, 0x67, 0x8d, 0xfa, 0x62, 0xc1, 0xb9, 0x9d, 0x30, 0xd0, 0x3d, 0xb8, 0x7c, - 0xa1, 0x51, 0x8f, 0x74, 0x71, 0x18, 0xd2, 0xc0, 0x9e, 0x33, 0x47, 0x59, 0xf7, 0x2f, 0xb0, 0xb9, - 0x9b, 0x88, 0xa1, 0x25, 0x98, 0x50, 0x3c, 0xf2, 0x1e, 0xda, 0xf3, 0x1b, 0xd6, 0xe6, 0xac, 0x5b, - 0x55, 0x3c, 0x7a, 0x88, 0xde, 0x82, 0xe5, 0x18, 0x07, 0xcc, 0xc7, 0x8a, 0x0b, 0xe9, 0x45, 0xfc, - 0x84, 0x0a, 0x8f, 0xe0, 0xc8, 0x5e, 0x30, 0x32, 0xa8, 0xe0, 0xed, 0x6b, 0xd6, 0x2e, 0x8e, 0xd0, - 0x9b, 0xb0, 0x98, 0x53, 0x3d, 0x49, 0x95, 0x11, 0x5f, 0x34, 0xe2, 0xf3, 0x39, 0xe3, 0x80, 0x2a, - 0x2d, 0xbb, 0x06, 0x35, 0x1c, 0x04, 0xfc, 0x24, 0x60, 0x52, 0xd9, 0x68, 0xa3, 0xb2, 0x59, 0x73, - 0x0b, 0x02, 0x5a, 0x85, 0x69, 0x9f, 0x86, 0x03, 0xc3, 0x5c, 0x32, 0xcc, 0xfc, 0xf9, 0xd6, 0xd5, - 0x5f, 0x7d, 0xbe, 0x3e, 0xf6, 0xd9, 0xe7, 0xeb, 0x63, 0xff, 0xf8, 0xcb, 0xf5, 0xd5, 0xb4, 0x63, - 0x74, 0x78, 0xdc, 0x4c, 0xbb, 0x4b, 0x73, 0x97, 0x87, 0x8a, 0x86, 0xca, 0xf9, 0xa7, 0x05, 0x97, - 0x76, 0xf3, 0x18, 0xf6, 0x78, 0x8c, 0x83, 0xef, 0xb2, 0x57, 0x6c, 0x43, 0x4d, 0xea, 0x4b, 0x34, - 0xd5, 0x59, 0x7d, 0x85, 0xea, 0x9c, 0xd6, 0x6a, 0x9a, 0x71, 0xab, 0xf1, 0x92, 0x13, 0xfd, 0x6e, - 0x1c, 0xd6, 0xb2, 0x13, 0x3d, 0xe0, 0x3e, 0x3b, 0x62, 0x04, 0x7f, 0xd7, 0x2d, 0x30, 0x4f, 0x8d, - 0xea, 0x08, 0xa9, 0x31, 0xf1, 0x6a, 0xa9, 0x31, 0x39, 0x42, 0x6a, 0x4c, 0xbd, 0x28, 0x35, 0xa6, - 0x87, 0x53, 0xc3, 0xf9, 0xbd, 0x05, 0xcb, 0xb7, 0x9f, 0xf6, 0x59, 0xcc, 0xff, 0x4f, 0x17, 0x73, - 0x1f, 0x66, 0x69, 0x09, 0x4f, 0xda, 0x95, 0x8d, 0xca, 0x66, 0xfd, 0xc6, 0x95, 0x66, 0x1a, 0xa5, - 0x7c, 0xda, 0x65, 0xa1, 0x2a, 0x5b, 0x77, 0x87, 0x75, 0x6f, 0x8d, 0xdb, 0x96, 0xf3, 0x37, 0x0b, - 0x56, 0x75, 0xd5, 0x75, 0xa8, 0x4b, 0x4f, 0xb0, 0xf0, 0xf7, 0x68, 0xc8, 0x7b, 0xf2, 0x5b, 0xfb, - 0xe9, 0xc0, 0xac, 0x6f, 0x90, 0x3c, 0xc5, 0x3d, 0xec, 0xfb, 0xc6, 0x4f, 0x23, 0xa3, 0x89, 0x87, - 0x7c, 0xdb, 0xf7, 0xd1, 0x26, 0x2c, 0x14, 0x32, 0x42, 0x17, 0x84, 0xce, 0x53, 0x2d, 0x36, 0x97, - 0x89, 0x99, 0x32, 0x79, 0x79, 0x1e, 0xfe, 0xd7, 0x82, 0x85, 0x0f, 0x02, 0xde, 0xc6, 0xc1, 0x41, - 0x80, 0x65, 0x57, 0x77, 0xa4, 0x81, 0xce, 0x7f, 0x41, 0xd3, 0x51, 0x60, 0xdc, 0x1f, 0x39, 0xff, - 0xb5, 0x9a, 0x19, 0x4e, 0xef, 0xc1, 0x62, 0xde, 0x9c, 0xf3, 0x7c, 0x34, 0xa7, 0xdd, 0x59, 0x7a, - 0xf6, 0xf5, 0xfa, 0x7c, 0x96, 0xfb, 0xbb, 0x26, 0x37, 0xf7, 0xdc, 0x79, 0x32, 0x44, 0xf0, 0x51, - 0x03, 0xea, 0xac, 0x4d, 0x3c, 0x49, 0x9f, 0x7a, 0x61, 0xbf, 0x67, 0x52, 0xb9, 0xea, 0xd6, 0x58, - 0x9b, 0x1c, 0xd0, 0xa7, 0x0f, 0xfb, 0x3d, 0xf4, 0x36, 0xbc, 0x9e, 0xad, 0x6c, 0x5e, 0x8c, 0x03, - 0x4f, 0xeb, 0xeb, 0xeb, 0x12, 0x26, 0xbb, 0x67, 0xdc, 0xa5, 0x8c, 0xfb, 0x18, 0x07, 0xda, 0xd8, - 0xb6, 0xef, 0x0b, 0xe7, 0xaf, 0x93, 0x30, 0xb9, 0x8f, 0x05, 0xee, 0x49, 0x74, 0x08, 0xf3, 0x8a, - 0xf6, 0xa2, 0x00, 0x2b, 0xea, 0x25, 0x83, 0x3f, 0x3d, 0xe9, 0x35, 0xb3, 0x10, 0x94, 0xd7, 0xab, - 0x66, 0x69, 0xa1, 0x8a, 0xb7, 0x9a, 0xbb, 0x86, 0x7a, 0xa0, 0xb0, 0xa2, 0xee, 0x5c, 0x86, 0x91, - 0x10, 0xd1, 0x4d, 0xb0, 0x95, 0xe8, 0x4b, 0x55, 0x8c, 0xe4, 0x62, 0x16, 0x25, 0xb1, 0x7e, 0x3d, - 0xe3, 0x27, 0x53, 0x2c, 0x9f, 0x41, 0xe7, 0x4f, 0xdf, 0xca, 0xb7, 0x99, 0xbe, 0x07, 0xb0, 0xa4, - 0x57, 0x97, 0xd3, 0x98, 0xd5, 0xd1, 0x31, 0x17, 0xb5, 0xfe, 0x30, 0xe8, 0x47, 0x80, 0x62, 0x49, - 0x4e, 0x63, 0x4e, 0xbc, 0x82, 0x9f, 0xb1, 0x24, 0xc3, 0x90, 0x3e, 0xac, 0x49, 0x9d, 0x7c, 0x5e, - 0x8f, 0x2a, 0x33, 0xcb, 0xa3, 0x80, 0x86, 0x4c, 0x76, 0x33, 0xf0, 0xc9, 0xd1, 0xc1, 0x57, 0x0c, - 0xd0, 0x03, 0x8d, 0xe3, 0x66, 0x30, 0xa9, 0x95, 0x5d, 0x68, 0x9c, 0x6f, 0x25, 0x0f, 0xd0, 0x94, - 0x09, 0xd0, 0xf7, 0xce, 0x81, 0xc8, 0xa3, 0x24, 0xe1, 0x6a, 0x69, 0xe7, 0xd0, 0x55, 0xef, 0x99, - 0x82, 0xf3, 0x04, 0xed, 0xe8, 0xc1, 0x8c, 0x93, 0xf5, 0x83, 0xd2, 0x7c, 0x6f, 0x4a, 0x6b, 0x4f, - 0x2f, 0xcd, 0xa5, 0xe2, 0x63, 0x61, 0xba, 0x5c, 0x3a, 0xc5, 0x6a, 0x92, 0xf7, 0x10, 0xb7, 0x84, - 0xf5, 0x3e, 0xa5, 0xba, 0xda, 0x4b, 0xeb, 0x09, 0x8d, 0x38, 0xe9, 0x9a, 0xf5, 0xa9, 0xe2, 0xce, - 0xe5, 0xab, 0xc8, 0x6d, 0x4d, 0x45, 0x1f, 0xc3, 0xb5, 0xb0, 0xdf, 0x6b, 0x53, 0xe1, 0xf1, 0xa3, - 0x44, 0xd0, 0x74, 0x08, 0xa9, 0xb0, 0x50, 0x9e, 0xa0, 0x84, 0xb2, 0x58, 0x67, 0x66, 0xe2, 0xb9, - 0x34, 0xdb, 0x51, 0xc5, 0xbd, 0x92, 0xa8, 0x3c, 0x3a, 0x32, 0x18, 0xf2, 0x90, 0x1f, 0x68, 0x71, - 0x37, 0x93, 0x4e, 0x1c, 0x93, 0xf7, 0xaa, 0xd3, 0xd3, 0x0b, 0x35, 0xe7, 0x47, 0x50, 0x33, 0x8d, - 0x62, 0x9b, 0x1c, 0x4b, 0xd3, 0xdd, 0x7d, 0x5f, 0x50, 0x29, 0xa9, 0xb4, 0xad, 0xb4, 0xbb, 0x67, - 0x04, 0x47, 0xc1, 0xca, 0x45, 0x0b, 0xbe, 0x44, 0x4f, 0x60, 0x2a, 0xa2, 0x66, 0xfb, 0x34, 0x8a, - 0xf5, 0x1b, 0xef, 0x36, 0x47, 0x78, 0x33, 0x6b, 0x5e, 0x04, 0xe8, 0x66, 0x68, 0x8e, 0x28, 0x5e, - 0x2b, 0x4e, 0x6d, 0x0a, 0x12, 0x3d, 0x3e, 0x6d, 0xf4, 0x27, 0xaf, 0x64, 0xf4, 0x14, 0x5e, 0x61, - 0xf3, 0x1a, 0xd4, 0xb7, 0x93, 0x63, 0x7f, 0xa8, 0xc7, 0xda, 0x99, 0x6b, 0x99, 0x29, 0x5f, 0xcb, - 0x3d, 0x98, 0x4b, 0x77, 0xb5, 0x43, 0x6e, 0x9a, 0x1d, 0xfa, 0x3e, 0x40, 0xba, 0xe4, 0xe9, 0x26, - 0x99, 0x8c, 0x8b, 0x5a, 0x4a, 0xb9, 0xeb, 0x0f, 0x4d, 0xf4, 0xf1, 0xa1, 0x89, 0xee, 0xb8, 0x30, - 0xff, 0x58, 0x92, 0x9f, 0x66, 0x8b, 0xfc, 0xa3, 0x48, 0xa2, 0xd7, 0x60, 0x52, 0xd7, 0x67, 0x0a, - 0x54, 0x75, 0x27, 0x62, 0x49, 0xee, 0x9a, 0x89, 0x51, 0xbc, 0x2c, 0xf0, 0xc8, 0x63, 0xbe, 0xb4, - 0xc7, 0x37, 0x2a, 0x9b, 0x55, 0x77, 0xae, 0x5f, 0xa8, 0xdf, 0xf5, 0xa5, 0xf3, 0x33, 0xa8, 0x97, - 0x00, 0xd1, 0x1c, 0x8c, 0xe7, 0x58, 0xe3, 0xcc, 0x47, 0xb7, 0x60, 0xa5, 0x00, 0x1a, 0x6e, 0xf1, - 0x09, 0x62, 0xcd, 0xbd, 0x94, 0x0b, 0x0c, 0x75, 0x79, 0xe9, 0x3c, 0x82, 0xe5, 0xbb, 0x45, 0x43, - 0xc9, 0x07, 0xc8, 0xd0, 0x09, 0xad, 0xe1, 0x9d, 0x65, 0x0d, 0x6a, 0xf9, 0xfb, 0xb3, 0x39, 0x7d, - 0xd5, 0x2d, 0x08, 0x4e, 0x0f, 0x16, 0x1e, 0x4b, 0x72, 0x40, 0x43, 0xbf, 0x00, 0xbb, 0xe0, 0x02, - 0x76, 0x4e, 0x03, 0x8d, 0xfc, 0xc6, 0x55, 0x98, 0xe3, 0xb0, 0xf2, 0xb8, 0xbc, 0xe0, 0x98, 0xe1, - 0xbf, 0x8f, 0xc9, 0x31, 0x55, 0x12, 0xb9, 0x50, 0x35, 0x8b, 0x4c, 0x92, 0x59, 0x37, 0x2f, 0xcc, - 0xac, 0x78, 0xab, 0x79, 0x11, 0xc8, 0x1e, 0x56, 0x38, 0xed, 0x0b, 0x06, 0xcb, 0xf9, 0x21, 0x2c, - 0x3d, 0xc0, 0xaa, 0x2f, 0xa8, 0x3f, 0x14, 0xe3, 0x05, 0xa8, 0xe8, 0xf8, 0x59, 0x26, 0x7e, 0xfa, - 0xa7, 0xf3, 0x47, 0x0b, 0xec, 0xdb, 0x9f, 0x44, 0x5c, 0x28, 0xea, 0x9f, 0xb9, 0x91, 0x17, 0x5c, - 0xef, 0x31, 0x2c, 0xe9, 0xcb, 0x92, 0x34, 0xf4, 0xbd, 0xfc, 0x9c, 0x49, 0x1c, 0xeb, 0x37, 0xde, - 0x19, 0xa9, 0x3a, 0x4e, 0x9b, 0x4b, 0x0f, 0xb0, 0x18, 0x9f, 0xa2, 0x4b, 0xe7, 0x37, 0x16, 0xd8, - 0xf7, 0xe9, 0x60, 0x5b, 0x4a, 0xd6, 0x09, 0x7b, 0x34, 0x54, 0xba, 0xbf, 0x62, 0x42, 0xf5, 0x4f, - 0xf4, 0x06, 0xcc, 0xe6, 0xf3, 0xdc, 0x8c, 0x71, 0xcb, 0x8c, 0xf1, 0x99, 0x8c, 0xa8, 0x0b, 0x0c, - 0xdd, 0x02, 0x88, 0x04, 0x8d, 0x3d, 0xe2, 0x1d, 0xd3, 0x41, 0x1a, 0xc5, 0xb5, 0xf2, 0x78, 0x4e, - 0xbe, 0x6e, 0x34, 0xf7, 0xfb, 0xed, 0x80, 0x91, 0xfb, 0x74, 0xe0, 0x4e, 0x6b, 0xf9, 0xdd, 0xfb, - 0x74, 0xa0, 0xf7, 0x31, 0xb3, 0xdd, 0x9a, 0x99, 0x5a, 0x71, 0x93, 0x07, 0xe7, 0xb7, 0x16, 0x5c, - 0xca, 0xc3, 0x91, 0xa5, 0xeb, 0x7e, 0xbf, 0xad, 0x35, 0x5e, 0x70, 0x6f, 0x67, 0xbc, 0x1d, 0x3f, - 0xc7, 0xdb, 0xf7, 0x60, 0x26, 0x2f, 0x10, 0xed, 0x6f, 0x65, 0x04, 0x7f, 0xeb, 0x99, 0xc6, 0x7d, - 0x3a, 0x70, 0x7e, 0x59, 0xf2, 0x6d, 0x67, 0x50, 0xea, 0x7d, 0xe2, 0x25, 0xbe, 0xe5, 0x66, 0xcb, - 0xbe, 0x91, 0xb2, 0xfe, 0x99, 0x03, 0x54, 0xce, 0x1e, 0xc0, 0xf9, 0x83, 0x05, 0xcb, 0x65, 0xab, - 0xf2, 0x90, 0xef, 0x8b, 0x7e, 0x48, 0x5f, 0x64, 0xbd, 0x28, 0xbf, 0xf1, 0x72, 0xf9, 0x3d, 0x81, - 0xb9, 0x21, 0xa7, 0x64, 0x7a, 0x1b, 0x6f, 0x8d, 0x94, 0x63, 0xa5, 0xee, 0xea, 0xce, 0x96, 0xcf, - 0x21, 0x9d, 0xbf, 0x5b, 0xb0, 0x98, 0xf9, 0x98, 0x5f, 0x16, 0xfa, 0x31, 0xa0, 0xfc, 0x78, 0xc5, - 0x66, 0x98, 0xa4, 0xd4, 0x42, 0xc6, 0xc9, 0xd6, 0xc2, 0x22, 0x35, 0xc6, 0x4b, 0xa9, 0x81, 0x3e, - 0x84, 0xa5, 0xdc, 0xe5, 0xc8, 0x04, 0x68, 0xe4, 0x28, 0xe6, 0xbb, 0x6f, 0x4e, 0x42, 0xeb, 0x50, - 0xff, 0x05, 0x67, 0x61, 0xf9, 0xe3, 0x53, 0xc5, 0x05, 0x4d, 0x4a, 0xbe, 0x2b, 0x39, 0xbf, 0xb6, - 0x8a, 0x79, 0x99, 0xce, 0xdc, 0xed, 0x20, 0x48, 0xdf, 0x38, 0x50, 0x04, 0x53, 0xd9, 0xd4, 0x4e, - 0x1a, 0xcc, 0xda, 0xb9, 0x9b, 0xc5, 0x1e, 0x25, 0x66, 0xb9, 0xb8, 0xa9, 0x6b, 0xf0, 0x8b, 0x6f, - 0xd6, 0xaf, 0x75, 0x98, 0xea, 0xf6, 0xdb, 0x4d, 0xc2, 0x7b, 0xe9, 0x17, 0xb9, 0xf4, 0xdf, 0x75, - 0xe9, 0x1f, 0xb7, 0xd4, 0x20, 0xa2, 0x32, 0xd3, 0x91, 0x7f, 0xfa, 0xcf, 0x9f, 0xdf, 0xb4, 0xdc, - 0xcc, 0xcc, 0xce, 0x93, 0x2f, 0x9f, 0x35, 0xac, 0xaf, 0x9e, 0x35, 0xac, 0x7f, 0x3f, 0x6b, 0x58, - 0x9f, 0x3e, 0x6f, 0x8c, 0x7d, 0xf5, 0xbc, 0x31, 0xf6, 0xaf, 0xe7, 0x8d, 0xb1, 0x8f, 0xdf, 0x3d, - 0x0b, 0x5a, 0x04, 0xf1, 0x7a, 0xfe, 0xc1, 0x34, 0x7e, 0xa7, 0xf5, 0xc9, 0xf0, 0xe7, 0x58, 0x63, - 0xaf, 0x3d, 0x69, 0xda, 0xed, 0xdb, 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x5f, 0xaa, 0x2b, 0x22, - 0xbf, 0x15, 0x00, 0x00, + // 1946 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcd, 0x6f, 0x1b, 0xc7, + 0x15, 0xd7, 0x8a, 0x94, 0x44, 0x0e, 0xf5, 0x39, 0x52, 0xec, 0x95, 0xa2, 0x92, 0x34, 0x53, 0x1b, + 0x6c, 0x5c, 0x2f, 0x23, 0x05, 0x05, 0x0c, 0xb7, 0x81, 0x21, 0x51, 0x4e, 0x6c, 0xa9, 0x71, 0xd8, + 0x15, 0xa1, 0x00, 0xe9, 0x61, 0x31, 0x9c, 0x1d, 0x91, 0x53, 0xed, 0xee, 0xac, 0x67, 0x86, 0x2b, + 0xf3, 0xd2, 0x73, 0x2f, 0x05, 0xd2, 0x5b, 0xd0, 0x4b, 0x73, 0x2c, 0x7a, 0xea, 0xa1, 0xe8, 0x1f, + 0xd0, 0x53, 0x50, 0xa0, 0x68, 0x8e, 0x3d, 0x25, 0x85, 0x7d, 0xe8, 0xa1, 0x40, 0xaf, 0xbd, 0x16, + 0x33, 0xfb, 0xc1, 0xa5, 0x3e, 0x1c, 0x1a, 0x69, 0x2e, 0xd2, 0xee, 0x7b, 0xbf, 0xf7, 0x9b, 0x37, + 0x33, 0xef, 0x8b, 0x0b, 0x76, 0x69, 0x20, 0x09, 0xc7, 0x03, 0x44, 0x03, 0x47, 0x10, 0x3c, 0xe4, + 0x54, 0x8e, 0x5a, 0x18, 0x47, 0xad, 0x90, 0xb3, 0x88, 0xba, 0x84, 0xb7, 0xa2, 0x9d, 0xec, 0xd9, + 0x0a, 0x39, 0x93, 0x0c, 0xbe, 0x75, 0x85, 0x8d, 0x85, 0x71, 0x64, 0x65, 0xb8, 0x68, 0x67, 0xeb, + 0xf6, 0x75, 0xc4, 0xd1, 0x4e, 0xeb, 0x9c, 0x72, 0x12, 0x73, 0x6d, 0x6d, 0xf4, 0x59, 0x9f, 0xe9, + 0xc7, 0x96, 0x7a, 0x4a, 0xa4, 0xb5, 0x3e, 0x63, 0x7d, 0x8f, 0xb4, 0xf4, 0x5b, 0x6f, 0x78, 0xda, + 0x92, 0xd4, 0x27, 0x42, 0x22, 0x3f, 0x4c, 0x00, 0xd5, 0x8b, 0x00, 0x77, 0xc8, 0x91, 0xa4, 0x2c, + 0x48, 0x09, 0x68, 0x0f, 0xb7, 0x30, 0xe3, 0xa4, 0x85, 0x3d, 0x4a, 0x02, 0xa9, 0x56, 0x8d, 0x9f, + 0x12, 0x40, 0x4b, 0x01, 0x3c, 0xda, 0x1f, 0xc8, 0x58, 0x2c, 0x5a, 0x92, 0x04, 0x2e, 0xe1, 0x3e, + 0x8d, 0xc1, 0xe3, 0xb7, 0xc4, 0x60, 0x3b, 0xa7, 0xc7, 0x7c, 0x14, 0x4a, 0xd6, 0x3a, 0x23, 0x23, + 0x91, 0x68, 0xef, 0x60, 0x26, 0x7c, 0x26, 0x5a, 0x44, 0xed, 0x3f, 0xc0, 0xa4, 0x15, 0xed, 0xf4, + 0x88, 0x44, 0x3b, 0x99, 0x20, 0xf5, 0x3b, 0xc1, 0xf5, 0x90, 0x18, 0x63, 0x30, 0xa3, 0xa9, 0xdf, + 0x9b, 0xb1, 0xde, 0x89, 0x4f, 0x24, 0x7e, 0x49, 0x54, 0x6b, 0xc8, 0xa7, 0x01, 0x6b, 0xe9, 0xbf, + 0xb1, 0xa8, 0xf1, 0xdf, 0x12, 0x30, 0xdb, 0x2c, 0x10, 0x43, 0x9f, 0xf0, 0x3d, 0xd7, 0xa5, 0xea, + 0x00, 0x3a, 0x9c, 0x85, 0x4c, 0x20, 0x0f, 0x6e, 0x80, 0x39, 0x49, 0xa5, 0x47, 0x4c, 0xa3, 0x6e, + 0x34, 0xcb, 0x76, 0xfc, 0x02, 0xeb, 0xa0, 0xe2, 0x12, 0x81, 0x39, 0x0d, 0x15, 0xd8, 0x9c, 0xd5, + 0xba, 0xbc, 0x08, 0x6e, 0x82, 0x52, 0x7c, 0x6b, 0xd4, 0x35, 0x0b, 0x5a, 0xbd, 0xa0, 0xdf, 0x9f, + 0xb8, 0xf0, 0x03, 0xb0, 0x4c, 0x03, 0x2a, 0x29, 0xf2, 0x9c, 0x01, 0x51, 0x67, 0x67, 0x16, 0xeb, + 0x46, 0xb3, 0xb2, 0xbb, 0x65, 0xd1, 0x1e, 0xb6, 0xd4, 0x71, 0x5b, 0xc9, 0x21, 0x47, 0x3b, 0xd6, + 0x63, 0x8d, 0xd8, 0x2f, 0x7e, 0xf1, 0x55, 0x6d, 0xc6, 0x5e, 0x4a, 0xec, 0x62, 0x21, 0xbc, 0x05, + 0x16, 0xfb, 0x24, 0x20, 0x82, 0x0a, 0x67, 0x80, 0xc4, 0xc0, 0x9c, 0xab, 0x1b, 0xcd, 0x45, 0xbb, + 0x92, 0xc8, 0x1e, 0x23, 0x31, 0x80, 0x35, 0x50, 0xe9, 0xd1, 0x00, 0xf1, 0x51, 0x8c, 0x98, 0xd7, + 0x08, 0x10, 0x8b, 0x34, 0xa0, 0x0d, 0x80, 0x08, 0xd1, 0x79, 0xe0, 0xa8, 0xd8, 0x30, 0x17, 0x12, + 0x47, 0xe2, 0xb8, 0xb0, 0xd2, 0xb8, 0xb0, 0xba, 0x69, 0xe0, 0xec, 0x97, 0x94, 0x23, 0x9f, 0x7e, + 0x5d, 0x33, 0xec, 0xb2, 0xb6, 0x53, 0x1a, 0xf8, 0x14, 0xac, 0x0e, 0x83, 0x1e, 0x0b, 0x5c, 0x1a, + 0xf4, 0x9d, 0x90, 0x70, 0xca, 0x5c, 0xb3, 0xa4, 0xa9, 0x36, 0x2f, 0x51, 0x1d, 0x24, 0x21, 0x16, + 0x33, 0x7d, 0xa6, 0x98, 0x56, 0x32, 0xe3, 0x8e, 0xb6, 0x85, 0x3f, 0x03, 0x10, 0xe3, 0x48, 0xbb, + 0xc4, 0x86, 0x32, 0x65, 0x2c, 0x4f, 0xcf, 0xb8, 0x8a, 0x71, 0xd4, 0x8d, 0xad, 0x13, 0xca, 0x9f, + 0x83, 0x9b, 0x92, 0xa3, 0x40, 0x9c, 0x12, 0x7e, 0x91, 0x17, 0x4c, 0xcf, 0xfb, 0x46, 0xca, 0x31, + 0x49, 0xfe, 0x18, 0xd4, 0x71, 0x12, 0x40, 0x0e, 0x27, 0x2e, 0x15, 0x92, 0xd3, 0xde, 0x50, 0xd9, + 0x3a, 0xa7, 0x1c, 0x61, 0x1d, 0x23, 0x15, 0x1d, 0x04, 0xd5, 0x14, 0x67, 0x4f, 0xc0, 0xde, 0x4f, + 0x50, 0xf0, 0x23, 0xf0, 0xfd, 0x9e, 0xc7, 0xf0, 0x99, 0x50, 0xce, 0x39, 0x13, 0x4c, 0x7a, 0x69, + 0x9f, 0x0a, 0xa1, 0xd8, 0x16, 0xeb, 0x46, 0xb3, 0x60, 0xdf, 0x8a, 0xb1, 0x1d, 0xc2, 0x0f, 0x72, + 0xc8, 0x6e, 0x0e, 0x08, 0xef, 0x01, 0x38, 0xa0, 0x42, 0x32, 0x4e, 0x31, 0xf2, 0x1c, 0x12, 0x48, + 0x4e, 0x89, 0x30, 0x97, 0xb4, 0xf9, 0xda, 0x58, 0xf3, 0x28, 0x56, 0xc0, 0x43, 0x70, 0xeb, 0xda, + 0x45, 0x1d, 0x3c, 0x40, 0x41, 0x40, 0x3c, 0x73, 0x59, 0x6f, 0xa5, 0xe6, 0x5e, 0xb3, 0x66, 0x3b, + 0x86, 0xc1, 0x75, 0x30, 0x27, 0x59, 0xe8, 0x3c, 0x35, 0x57, 0xea, 0x46, 0x73, 0xc9, 0x2e, 0x4a, + 0x16, 0x3e, 0x85, 0xef, 0x80, 0x8d, 0x08, 0x79, 0xd4, 0x45, 0x92, 0x71, 0xe1, 0x84, 0xec, 0x9c, + 0x70, 0x07, 0xa3, 0xd0, 0x5c, 0xd5, 0x18, 0x38, 0xd6, 0x75, 0x94, 0xaa, 0x8d, 0x42, 0xf8, 0x36, + 0x58, 0xcb, 0xa4, 0x8e, 0x20, 0x52, 0xc3, 0xd7, 0x34, 0x7c, 0x25, 0x53, 0x1c, 0x13, 0xa9, 0xb0, + 0xdb, 0xa0, 0x8c, 0x3c, 0x8f, 0x9d, 0x7b, 0x54, 0x48, 0x13, 0xd6, 0x0b, 0xcd, 0xb2, 0x3d, 0x16, + 0xc0, 0x2d, 0x50, 0x72, 0x49, 0x30, 0xd2, 0xca, 0x75, 0xad, 0xcc, 0xde, 0xe1, 0x9b, 0xa0, 0xec, + 0xab, 0x1a, 0x2b, 0xd1, 0x19, 0x31, 0x37, 0xea, 0x46, 0xb3, 0x68, 0x97, 0x7c, 0x1a, 0x1c, 0xab, + 0x77, 0x68, 0x81, 0x75, 0xcd, 0xe2, 0xd0, 0x40, 0xdd, 0x53, 0x44, 0x9c, 0x08, 0x79, 0xc2, 0x7c, + 0xa3, 0x6e, 0x34, 0x4b, 0xf6, 0x9a, 0x56, 0x3d, 0x49, 0x34, 0x27, 0xc8, 0x13, 0x0f, 0xee, 0xfc, + 0xea, 0xf3, 0xda, 0xcc, 0x67, 0x9f, 0xd7, 0x66, 0xfe, 0xfa, 0xa7, 0x7b, 0x5b, 0x49, 0xf9, 0xe9, + 0xb3, 0xc8, 0x4a, 0x4a, 0x95, 0xd5, 0x66, 0x81, 0x24, 0x81, 0x6c, 0xfc, 0xdd, 0x00, 0x37, 0xdb, + 0x59, 0x40, 0xf8, 0x2c, 0x42, 0xde, 0x77, 0x59, 0x78, 0xf6, 0x40, 0x59, 0xa8, 0x1b, 0xd1, 0xa9, + 0x5e, 0x7c, 0x8d, 0x54, 0x2f, 0x29, 0x33, 0xa5, 0x78, 0x50, 0xfd, 0x86, 0x1d, 0xfd, 0x7b, 0x16, + 0x6c, 0xa7, 0x3b, 0xfa, 0x90, 0xb9, 0xf4, 0x94, 0x62, 0xf4, 0x5d, 0xd7, 0xd3, 0x2c, 0xce, 0x8a, + 0x53, 0xc4, 0xd9, 0xdc, 0xeb, 0xc5, 0xd9, 0xfc, 0x14, 0x71, 0xb6, 0xf0, 0xaa, 0x38, 0x2b, 0xbd, + 0x2a, 0xce, 0xca, 0xd3, 0xc5, 0x19, 0xb8, 0x26, 0xce, 0x1a, 0xbf, 0x33, 0xc0, 0xc6, 0xa3, 0x67, + 0x43, 0x1a, 0xb1, 0xff, 0xd3, 0x29, 0x1f, 0x81, 0x25, 0x92, 0xe3, 0x13, 0x66, 0xa1, 0x5e, 0x68, + 0x56, 0x76, 0x6f, 0x5b, 0xc9, 0x95, 0x67, 0x7d, 0x38, 0xbd, 0xf7, 0xfc, 0xea, 0xf6, 0xa4, 0xed, + 0x83, 0x59, 0xd3, 0x68, 0xfc, 0xc5, 0x00, 0x5b, 0xaa, 0x1e, 0xf4, 0x89, 0x4d, 0xce, 0x11, 0x77, + 0x0f, 0x48, 0xc0, 0x7c, 0xf1, 0xad, 0xfd, 0x6c, 0x80, 0x25, 0x57, 0x33, 0x39, 0x92, 0x39, 0xc8, + 0x75, 0xb5, 0x9f, 0x1a, 0xa3, 0x84, 0x5d, 0xb6, 0xe7, 0xba, 0xb0, 0x09, 0x56, 0xc7, 0x18, 0xae, + 0xb2, 0x4b, 0x05, 0xbd, 0x82, 0x2d, 0xa7, 0x30, 0x9d, 0x73, 0x53, 0x04, 0xb5, 0x01, 0x56, 0x3f, + 0xf0, 0x58, 0x0f, 0x79, 0xc7, 0x1e, 0x12, 0x03, 0x55, 0x2b, 0x47, 0x2a, 0x99, 0x38, 0x49, 0x9a, + 0x94, 0x76, 0x7f, 0xea, 0x64, 0x52, 0x66, 0xba, 0x6d, 0x3e, 0x04, 0x6b, 0x59, 0xdb, 0xc8, 0x82, + 0x5b, 0xef, 0x76, 0x7f, 0xfd, 0xc5, 0x57, 0xb5, 0x95, 0x34, 0x91, 0xda, 0x3a, 0xd0, 0x0f, 0xec, + 0x15, 0x3c, 0x21, 0x70, 0x61, 0x15, 0x54, 0x68, 0x0f, 0x3b, 0x82, 0x3c, 0x73, 0x82, 0xa1, 0xaf, + 0xf3, 0xa2, 0x68, 0x97, 0x69, 0x0f, 0x1f, 0x93, 0x67, 0x4f, 0x87, 0x3e, 0x7c, 0x17, 0xdc, 0x48, + 0x87, 0x49, 0x15, 0x49, 0x8e, 0xb2, 0x57, 0xc7, 0xc5, 0x75, 0xaa, 0x2c, 0xda, 0xeb, 0xa9, 0xf6, + 0x04, 0x79, 0x6a, 0xb1, 0x3d, 0xd7, 0xe5, 0x8d, 0xff, 0xcc, 0x81, 0xf9, 0x0e, 0xe2, 0xc8, 0x17, + 0xb0, 0x0b, 0x56, 0x24, 0xf1, 0x43, 0x0f, 0x49, 0xe2, 0xc4, 0x23, 0x49, 0xb2, 0xd3, 0xbb, 0x7a, + 0x54, 0xc9, 0x0f, 0x7e, 0x56, 0x6e, 0xd4, 0x8b, 0x76, 0xac, 0xb6, 0x96, 0x1e, 0x4b, 0x24, 0x89, + 0xbd, 0x9c, 0x72, 0xc4, 0x42, 0x78, 0x1f, 0x98, 0x92, 0x0f, 0x85, 0x1c, 0x0f, 0x0b, 0xe3, 0x2e, + 0x19, 0xdf, 0xf5, 0x8d, 0x54, 0x1f, 0xf7, 0xd7, 0xac, 0x3b, 0x5e, 0x3d, 0x17, 0x14, 0xbe, 0xcd, + 0x5c, 0xe0, 0x82, 0x6d, 0xa1, 0x2e, 0xd5, 0xf1, 0x89, 0xd4, 0xdd, 0x3b, 0xf4, 0x48, 0x40, 0xc5, + 0x20, 0x25, 0x9f, 0x9f, 0x9e, 0x7c, 0x53, 0x13, 0x7d, 0xa8, 0x78, 0xec, 0x94, 0x26, 0x59, 0xa5, + 0x0d, 0xaa, 0x57, 0xaf, 0x92, 0x6d, 0x7c, 0x41, 0x6f, 0xfc, 0xcd, 0x2b, 0x28, 0xb2, 0xdd, 0x0b, + 0x70, 0x27, 0x37, 0x65, 0xa8, 0x6c, 0x72, 0x74, 0x20, 0x3b, 0x9c, 0xf4, 0x55, 0x2b, 0x46, 0xf1, + 0xc0, 0x41, 0x48, 0x36, 0x29, 0x25, 0x31, 0xad, 0xc6, 0xe4, 0x5c, 0x50, 0xd3, 0x20, 0x19, 0x27, + 0x1b, 0xe3, 0x61, 0x24, 0xcb, 0x4d, 0x3b, 0xc7, 0xf5, 0x3e, 0x21, 0x2a, 0x8b, 0x72, 0x03, 0x09, + 0x09, 0x19, 0x1e, 0xe8, 0x7a, 0x54, 0xb0, 0x97, 0xb3, 0xe1, 0xe3, 0x91, 0x92, 0xc2, 0x4f, 0xc0, + 0xdd, 0x60, 0xe8, 0xf7, 0x08, 0x77, 0xd8, 0x69, 0x0c, 0xd4, 0x99, 0x27, 0x24, 0xe2, 0xd2, 0xe1, + 0x04, 0x13, 0x1a, 0xa9, 0x1b, 0x8f, 0x3d, 0x17, 0x7a, 0x1e, 0x2a, 0xd8, 0xb7, 0x63, 0x93, 0x8f, + 0x4e, 0x35, 0x87, 0xe8, 0xb2, 0x63, 0x05, 0xb7, 0x53, 0x74, 0xec, 0x98, 0x80, 0x4f, 0xc0, 0x2d, + 0x1f, 0x3d, 0x77, 0xb2, 0x60, 0x56, 0x8e, 0x93, 0x40, 0x0c, 0x85, 0x33, 0x2e, 0xe4, 0xc9, 0x4c, + 0x54, 0xf5, 0xd1, 0xf3, 0x4e, 0x82, 0x6b, 0xa7, 0xb0, 0x93, 0x0c, 0x75, 0x58, 0x2c, 0x15, 0x57, + 0xe7, 0x0e, 0x8b, 0xa5, 0xb9, 0xd5, 0xf9, 0xc3, 0x62, 0xa9, 0xb4, 0x5a, 0x6e, 0xfc, 0x00, 0x94, + 0x75, 0x5e, 0xef, 0xe1, 0x33, 0xa1, 0x2b, 0xbb, 0xeb, 0x72, 0x22, 0x04, 0x11, 0xa6, 0x91, 0x54, + 0xf6, 0x54, 0xd0, 0x90, 0x60, 0xf3, 0xba, 0x5f, 0x0a, 0x02, 0x7e, 0x0c, 0x16, 0x42, 0xa2, 0xc7, + 0x58, 0x6d, 0x58, 0xd9, 0x7d, 0xcf, 0x9a, 0xe2, 0x27, 0x9e, 0x75, 0x1d, 0xa1, 0x9d, 0xb2, 0x35, + 0xf8, 0xf8, 0xf7, 0xc9, 0x85, 0x29, 0x41, 0xc0, 0x93, 0x8b, 0x8b, 0xfe, 0xe4, 0xb5, 0x16, 0xbd, + 0xc0, 0x37, 0x5e, 0xf3, 0x2e, 0xa8, 0xec, 0xc5, 0xdb, 0xfe, 0xa9, 0x6a, 0x5b, 0x97, 0x8e, 0x65, + 0x31, 0x7f, 0x2c, 0x87, 0x60, 0x39, 0x19, 0xfa, 0xba, 0x4c, 0xd7, 0x26, 0xf8, 0x3d, 0x00, 0x92, + 0x69, 0x51, 0xd5, 0xb4, 0xb8, 0xba, 0x97, 0x13, 0xc9, 0x13, 0x77, 0xa2, 0x9b, 0xcf, 0x4e, 0x74, + 0xf3, 0x06, 0x03, 0x9b, 0x27, 0xf9, 0x6e, 0xab, 0x9b, 0x47, 0x07, 0xe1, 0x33, 0x22, 0x05, 0xb4, + 0x41, 0x51, 0x77, 0xd5, 0x78, 0xab, 0xf7, 0xaf, 0xdd, 0x6a, 0xb4, 0x63, 0x5d, 0x47, 0x72, 0x80, + 0x24, 0x4a, 0xe2, 0x5f, 0x73, 0x35, 0x7e, 0x63, 0x00, 0xf3, 0x88, 0x8c, 0xf6, 0x84, 0xa0, 0xfd, + 0xc0, 0x27, 0x81, 0x54, 0x99, 0x87, 0x30, 0x51, 0x8f, 0xf0, 0x2d, 0xb0, 0x94, 0x05, 0x9d, 0x2e, + 0x9c, 0x86, 0x2e, 0x9c, 0x8b, 0xa9, 0x50, 0x9d, 0x11, 0x7c, 0x00, 0x40, 0xc8, 0x49, 0xe4, 0x60, + 0xe7, 0x8c, 0x8c, 0xf4, 0x7e, 0x2a, 0xbb, 0xdb, 0xf9, 0x82, 0x18, 0xff, 0xd2, 0xb5, 0x3a, 0xc3, + 0x9e, 0x47, 0xf1, 0x11, 0x19, 0xd9, 0x25, 0x85, 0x6f, 0x1f, 0x91, 0x91, 0xea, 0x80, 0x7a, 0x38, + 0xd1, 0x55, 0xac, 0x60, 0xc7, 0x2f, 0x8d, 0xdf, 0x1a, 0xe0, 0x66, 0xb6, 0x81, 0xf4, 0xae, 0x3a, + 0xc3, 0x9e, 0xb2, 0xc8, 0x9f, 0x9d, 0x31, 0x39, 0x09, 0x5d, 0xf2, 0x76, 0xf6, 0x0a, 0x6f, 0x1f, + 0x82, 0xc5, 0xac, 0x8c, 0x28, 0x7f, 0x0b, 0x53, 0xf8, 0x5b, 0x49, 0x2d, 0x8e, 0xc8, 0xa8, 0xf1, + 0xcb, 0x9c, 0x6f, 0xfb, 0xa3, 0x5c, 0xf8, 0xf2, 0x6f, 0xf0, 0x2d, 0x5b, 0x36, 0xef, 0x1b, 0xce, + 0xdb, 0x5f, 0xda, 0x40, 0xe1, 0xf2, 0x06, 0x1a, 0x7f, 0x33, 0xc0, 0x8d, 0xfc, 0xaa, 0xa2, 0xcb, + 0x3a, 0x7c, 0x18, 0x90, 0x93, 0xdd, 0x57, 0xad, 0xff, 0x10, 0x94, 0x42, 0x85, 0x72, 0xa4, 0x48, + 0xae, 0x68, 0xba, 0x76, 0xbd, 0xa0, 0xad, 0xba, 0x2a, 0xbd, 0x97, 0x27, 0x36, 0x20, 0x92, 0x93, + 0x7b, 0x67, 0xaa, 0x84, 0xcb, 0x25, 0x93, 0xbd, 0x94, 0xdf, 0xb3, 0x68, 0xfc, 0xd9, 0x00, 0xf0, + 0x72, 0xa5, 0x82, 0x3f, 0x04, 0x70, 0xa2, 0xde, 0xe5, 0xe3, 0x6f, 0x35, 0xcc, 0x55, 0x38, 0x7d, + 0x72, 0x59, 0x1c, 0xcd, 0xe6, 0xe2, 0x08, 0xfe, 0x18, 0x80, 0x50, 0x5f, 0xe2, 0xd4, 0x37, 0x5d, + 0x0e, 0xd3, 0x47, 0x58, 0x03, 0x95, 0x5f, 0x30, 0x1a, 0xe4, 0x3f, 0x52, 0x14, 0x6c, 0xa0, 0x44, + 0xf1, 0xf7, 0x87, 0xc6, 0xaf, 0x8d, 0x71, 0x39, 0x4c, 0x2a, 0xf5, 0x9e, 0xe7, 0x25, 0xf3, 0x1f, + 0x0c, 0xc1, 0x42, 0x5a, 0xeb, 0xe3, 0x74, 0xdd, 0xbe, 0xb2, 0x1f, 0x1d, 0x10, 0xac, 0x5b, 0xd2, + 0x7d, 0x75, 0xe2, 0x7f, 0xf8, 0xba, 0x76, 0xb7, 0x4f, 0xe5, 0x60, 0xd8, 0xb3, 0x30, 0xf3, 0x93, + 0x2f, 0x37, 0xc9, 0xbf, 0x7b, 0xc2, 0x3d, 0x6b, 0xc9, 0x51, 0x48, 0x44, 0x6a, 0x23, 0x7e, 0xff, + 0xaf, 0x3f, 0xbe, 0x6d, 0xd8, 0xe9, 0x32, 0xfb, 0x1f, 0x7f, 0xf1, 0xa2, 0x6a, 0x7c, 0xf9, 0xa2, + 0x6a, 0xfc, 0xf3, 0x45, 0xd5, 0xf8, 0xf4, 0x65, 0x75, 0xe6, 0xcb, 0x97, 0xd5, 0x99, 0x7f, 0xbc, + 0xac, 0xce, 0x7c, 0xf2, 0xde, 0x65, 0xd2, 0xf1, 0xa5, 0xdd, 0xcb, 0x3e, 0xac, 0x45, 0x3f, 0x6a, + 0x3d, 0x9f, 0xfc, 0x6c, 0xa7, 0xd7, 0xeb, 0xcd, 0xeb, 0x08, 0x79, 0xf7, 0x7f, 0x01, 0x00, 0x00, + 0xff, 0xff, 0x8d, 0xfa, 0x87, 0x04, 0xe7, 0x13, 0x00, 0x00, } func (m *ConsumerAdditionProposal) Marshal() (dAtA []byte, err error) { @@ -1856,6 +1544,25 @@ func (m *ConsumerAdditionProposal) MarshalToSizedBuffer(dAtA []byte) (int, error _ = i var l int _ = l + if m.AllowInactiveVals { + i-- + if m.AllowInactiveVals { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa8 + } + if m.MinStake != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.MinStake)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa0 + } if len(m.Denylist) > 0 { for iNdEx := len(m.Denylist) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Denylist[iNdEx]) @@ -2073,6 +1780,21 @@ func (m *ConsumerModificationProposal) MarshalToSizedBuffer(dAtA []byte) (int, e _ = i var l int _ = l + if m.AllowInactiveVals { + i-- + if m.AllowInactiveVals { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x50 + } + if m.MinStake != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.MinStake)) + i-- + dAtA[i] = 0x48 + } if len(m.Denylist) > 0 { for iNdEx := len(m.Denylist) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Denylist[iNdEx]) @@ -2306,6 +2028,11 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.MaxProviderConsensusValidators != 0 { + i = encodeVarintProvider(dAtA, i, uint64(m.MaxProviderConsensusValidators)) + i-- + dAtA[i] = 0x60 + } if m.NumberOfEpochsToStartReceivingRewards != 0 { i = encodeVarintProvider(dAtA, i, uint64(m.NumberOfEpochsToStartReceivingRewards)) i-- @@ -2341,29 +2068,13 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintProvider(dAtA, i, uint64(n9)) i-- dAtA[i] = 0x32 - n10, err10 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.VscTimeoutPeriod, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.VscTimeoutPeriod):]) + n10, err10 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.CcvTimeoutPeriod, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.CcvTimeoutPeriod):]) if err10 != nil { return 0, err10 } i -= n10 i = encodeVarintProvider(dAtA, i, uint64(n10)) i-- - dAtA[i] = 0x2a - n11, err11 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.InitTimeoutPeriod, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.InitTimeoutPeriod):]) - if err11 != nil { - return 0, err11 - } - i -= n11 - i = encodeVarintProvider(dAtA, i, uint64(n11)) - i-- - dAtA[i] = 0x22 - n12, err12 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.CcvTimeoutPeriod, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.CcvTimeoutPeriod):]) - if err12 != nil { - return 0, err12 - } - i -= n12 - i = encodeVarintProvider(dAtA, i, uint64(n12)) - i-- dAtA[i] = 0x1a if len(m.TrustingPeriodFraction) > 0 { i -= len(m.TrustingPeriodFraction) @@ -2562,7 +2273,7 @@ func (m *ChannelToChain) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *VscUnbondingOps) Marshal() (dAtA []byte, err error) { +func (m *ValidatorSetChangePackets) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -2572,166 +2283,12 @@ func (m *VscUnbondingOps) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *VscUnbondingOps) MarshalTo(dAtA []byte) (int, error) { +func (m *ValidatorSetChangePackets) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *VscUnbondingOps) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.UnbondingOpIds) > 0 { - dAtA15 := make([]byte, len(m.UnbondingOpIds)*10) - var j14 int - for _, num := range m.UnbondingOpIds { - for num >= 1<<7 { - dAtA15[j14] = uint8(uint64(num)&0x7f | 0x80) - num >>= 7 - j14++ - } - dAtA15[j14] = uint8(num) - j14++ - } - i -= j14 - copy(dAtA[i:], dAtA15[:j14]) - i = encodeVarintProvider(dAtA, i, uint64(j14)) - i-- - dAtA[i] = 0x12 - } - if m.VscId != 0 { - i = encodeVarintProvider(dAtA, i, uint64(m.VscId)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *UnbondingOp) 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 *UnbondingOp) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *UnbondingOp) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.UnbondingConsumerChains) > 0 { - for iNdEx := len(m.UnbondingConsumerChains) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.UnbondingConsumerChains[iNdEx]) - copy(dAtA[i:], m.UnbondingConsumerChains[iNdEx]) - i = encodeVarintProvider(dAtA, i, uint64(len(m.UnbondingConsumerChains[iNdEx]))) - i-- - dAtA[i] = 0x12 - } - } - if m.Id != 0 { - i = encodeVarintProvider(dAtA, i, uint64(m.Id)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *InitTimeoutTimestamp) 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 *InitTimeoutTimestamp) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *InitTimeoutTimestamp) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Timestamp != 0 { - i = encodeVarintProvider(dAtA, i, uint64(m.Timestamp)) - i-- - dAtA[i] = 0x10 - } - 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] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *VscSendTimestamp) 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 *VscSendTimestamp) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *VscSendTimestamp) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - n16, err16 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp):]) - if err16 != nil { - return 0, err16 - } - i -= n16 - i = encodeVarintProvider(dAtA, i, uint64(n16)) - i-- - dAtA[i] = 0x12 - if m.VscId != 0 { - i = encodeVarintProvider(dAtA, i, uint64(m.VscId)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *ValidatorSetChangePackets) 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 *ValidatorSetChangePackets) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ValidatorSetChangePackets) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *ValidatorSetChangePackets) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -2753,91 +2310,6 @@ func (m *ValidatorSetChangePackets) MarshalToSizedBuffer(dAtA []byte) (int, erro return len(dAtA) - i, nil } -func (m *MaturedUnbondingOps) 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 *MaturedUnbondingOps) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *MaturedUnbondingOps) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Ids) > 0 { - dAtA18 := make([]byte, len(m.Ids)*10) - var j17 int - for _, num := range m.Ids { - for num >= 1<<7 { - dAtA18[j17] = uint8(uint64(num)&0x7f | 0x80) - num >>= 7 - j17++ - } - dAtA18[j17] = uint8(num) - j17++ - } - i -= j17 - copy(dAtA[i:], dAtA18[:j17]) - i = encodeVarintProvider(dAtA, i, uint64(j17)) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *ExportedVscSendTimestamp) 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 *ExportedVscSendTimestamp) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *ExportedVscSendTimestamp) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.VscSendTimestamps) > 0 { - for iNdEx := len(m.VscSendTimestamps) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.VscSendTimestamps[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintProvider(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - } - 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] = 0xa - } - return len(dAtA) - i, nil -} - func (m *KeyAssignmentReplacement) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -2978,7 +2450,7 @@ func (m *ValidatorByConsumerAddr) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } -func (m *ConsumerAddrsToPrune) Marshal() (dAtA []byte, err error) { +func (m *ConsumerAddrsToPruneV2) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -2988,12 +2460,12 @@ func (m *ConsumerAddrsToPrune) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *ConsumerAddrsToPrune) MarshalTo(dAtA []byte) (int, error) { +func (m *ConsumerAddrsToPruneV2) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *ConsumerAddrsToPrune) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *ConsumerAddrsToPruneV2) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -3010,11 +2482,14 @@ func (m *ConsumerAddrsToPrune) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x1a } - if m.VscId != 0 { - i = encodeVarintProvider(dAtA, i, uint64(m.VscId)) - i-- - dAtA[i] = 0x10 + n15, err15 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.PruneTs, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.PruneTs):]) + if err15 != nil { + return 0, err15 } + i -= n15 + i = encodeVarintProvider(dAtA, i, uint64(n15)) + i-- + dAtA[i] = 0x12 if len(m.ChainId) > 0 { i -= len(m.ChainId) copy(dAtA[i:], m.ChainId) @@ -3025,7 +2500,7 @@ func (m *ConsumerAddrsToPrune) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *ConsumerValidator) Marshal() (dAtA []byte, err error) { +func (m *ConsensusValidator) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -3035,12 +2510,12 @@ func (m *ConsumerValidator) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *ConsumerValidator) MarshalTo(dAtA []byte) (int, error) { +func (m *ConsensusValidator) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *ConsumerValidator) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *ConsensusValidator) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -3050,9 +2525,9 @@ func (m *ConsumerValidator) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x20 } - if m.ConsumerPublicKey != nil { + if m.PublicKey != nil { { - size, err := m.ConsumerPublicKey.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.PublicKey.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -3196,6 +2671,12 @@ func (m *ConsumerAdditionProposal) Size() (n int) { n += 2 + l + sovProvider(uint64(l)) } } + if m.MinStake != 0 { + n += 2 + sovProvider(uint64(m.MinStake)) + } + if m.AllowInactiveVals { + n += 3 + } return n } @@ -3261,6 +2742,12 @@ func (m *ConsumerModificationProposal) Size() (n int) { n += 1 + l + sovProvider(uint64(l)) } } + if m.MinStake != 0 { + n += 1 + sovProvider(uint64(m.MinStake)) + } + if m.AllowInactiveVals { + n += 2 + } return n } @@ -3354,10 +2841,6 @@ func (m *Params) Size() (n int) { } l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.CcvTimeoutPeriod) n += 1 + l + sovProvider(uint64(l)) - l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.InitTimeoutPeriod) - n += 1 + l + sovProvider(uint64(l)) - l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.VscTimeoutPeriod) - n += 1 + l + sovProvider(uint64(l)) l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.SlashMeterReplenishPeriod) n += 1 + l + sovProvider(uint64(l)) l = len(m.SlashMeterReplenishFraction) @@ -3372,6 +2855,9 @@ func (m *Params) Size() (n int) { if m.NumberOfEpochsToStartReceivingRewards != 0 { n += 1 + sovProvider(uint64(m.NumberOfEpochsToStartReceivingRewards)) } + if m.MaxProviderConsensusValidators != 0 { + n += 1 + sovProvider(uint64(m.MaxProviderConsensusValidators)) + } return n } @@ -3452,73 +2938,6 @@ func (m *ChannelToChain) Size() (n int) { return n } -func (m *VscUnbondingOps) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.VscId != 0 { - n += 1 + sovProvider(uint64(m.VscId)) - } - if len(m.UnbondingOpIds) > 0 { - l = 0 - for _, e := range m.UnbondingOpIds { - l += sovProvider(uint64(e)) - } - n += 1 + sovProvider(uint64(l)) + l - } - return n -} - -func (m *UnbondingOp) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Id != 0 { - n += 1 + sovProvider(uint64(m.Id)) - } - if len(m.UnbondingConsumerChains) > 0 { - for _, s := range m.UnbondingConsumerChains { - l = len(s) - n += 1 + l + sovProvider(uint64(l)) - } - } - return n -} - -func (m *InitTimeoutTimestamp) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ChainId) - if l > 0 { - n += 1 + l + sovProvider(uint64(l)) - } - if m.Timestamp != 0 { - n += 1 + sovProvider(uint64(m.Timestamp)) - } - return n -} - -func (m *VscSendTimestamp) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.VscId != 0 { - n += 1 + sovProvider(uint64(m.VscId)) - } - l = github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp) - n += 1 + l + sovProvider(uint64(l)) - return n -} - func (m *ValidatorSetChangePackets) Size() (n int) { if m == nil { return 0 @@ -3534,41 +2953,6 @@ func (m *ValidatorSetChangePackets) Size() (n int) { return n } -func (m *MaturedUnbondingOps) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Ids) > 0 { - l = 0 - for _, e := range m.Ids { - l += sovProvider(uint64(e)) - } - n += 1 + sovProvider(uint64(l)) + l - } - return n -} - -func (m *ExportedVscSendTimestamp) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ChainId) - if l > 0 { - n += 1 + l + sovProvider(uint64(l)) - } - if len(m.VscSendTimestamps) > 0 { - for _, e := range m.VscSendTimestamps { - l = e.Size() - n += 1 + l + sovProvider(uint64(l)) - } - } - return n -} - func (m *KeyAssignmentReplacement) Size() (n int) { if m == nil { return 0 @@ -3631,7 +3015,7 @@ func (m *ValidatorByConsumerAddr) Size() (n int) { return n } -func (m *ConsumerAddrsToPrune) Size() (n int) { +func (m *ConsumerAddrsToPruneV2) Size() (n int) { if m == nil { return 0 } @@ -3641,9 +3025,8 @@ func (m *ConsumerAddrsToPrune) Size() (n int) { if l > 0 { n += 1 + l + sovProvider(uint64(l)) } - if m.VscId != 0 { - n += 1 + sovProvider(uint64(m.VscId)) - } + l = github_com_cosmos_gogoproto_types.SizeOfStdTime(m.PruneTs) + n += 1 + l + sovProvider(uint64(l)) if m.ConsumerAddrs != nil { l = m.ConsumerAddrs.Size() n += 1 + l + sovProvider(uint64(l)) @@ -3651,7 +3034,7 @@ func (m *ConsumerAddrsToPrune) Size() (n int) { return n } -func (m *ConsumerValidator) Size() (n int) { +func (m *ConsensusValidator) Size() (n int) { if m == nil { return 0 } @@ -3664,8 +3047,8 @@ func (m *ConsumerValidator) Size() (n int) { if m.Power != 0 { n += 1 + sovProvider(uint64(m.Power)) } - if m.ConsumerPublicKey != nil { - l = m.ConsumerPublicKey.Size() + if m.PublicKey != nil { + l = m.PublicKey.Size() n += 1 + l + sovProvider(uint64(l)) } if m.JoinHeight != 0 { @@ -4276,13 +3659,52 @@ func (m *ConsumerAdditionProposal) Unmarshal(dAtA []byte) error { } m.Denylist = append(m.Denylist, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipProvider(dAtA[iNdEx:]) - if err != nil { - return err + case 20: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MinStake", wireType) } - if (skippy < 0) || (iNdEx+skippy) < 0 { + m.MinStake = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MinStake |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 21: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowInactiveVals", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowInactiveVals = bool(v != 0) + 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 { @@ -4722,6 +4144,45 @@ func (m *ConsumerModificationProposal) Unmarshal(dAtA []byte) error { } m.Denylist = append(m.Denylist, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MinStake", wireType) + } + m.MinStake = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MinStake |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowInactiveVals", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowInactiveVals = bool(v != 0) default: iNdEx = preIndex skippy, err := skipProvider(dAtA[iNdEx:]) @@ -5367,72 +4828,6 @@ func (m *Params) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field InitTimeoutPeriod", 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 - } - if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.InitTimeoutPeriod, dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field VscTimeoutPeriod", 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 - } - if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.VscTimeoutPeriod, dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SlashMeterReplenishPeriod", wireType) @@ -5569,6 +4964,25 @@ func (m *Params) Unmarshal(dAtA []byte) error { break } } + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxProviderConsensusValidators", wireType) + } + m.MaxProviderConsensusValidators = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProvider + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxProviderConsensusValidators |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipProvider(dAtA[iNdEx:]) @@ -6036,7 +5450,7 @@ func (m *ChannelToChain) Unmarshal(dAtA []byte) error { } return nil } -func (m *VscUnbondingOps) Unmarshal(dAtA []byte) error { +func (m *ValidatorSetChangePackets) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -6059,17 +5473,17 @@ func (m *VscUnbondingOps) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: VscUnbondingOps: wiretype end group for non-group") + return fmt.Errorf("proto: ValidatorSetChangePackets: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: VscUnbondingOps: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ValidatorSetChangePackets: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field VscId", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field List", wireType) } - m.VscId = 0 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowProvider @@ -6079,87 +5493,26 @@ func (m *VscUnbondingOps) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.VscId |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - case 2: - if wireType == 0 { - var v uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProvider - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.UnbondingOpIds = append(m.UnbondingOpIds, v) - } else if wireType == 2 { - var packedLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProvider - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - packedLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if packedLen < 0 { - return ErrInvalidLengthProvider - } - postIndex := iNdEx + packedLen - if postIndex < 0 { - return ErrInvalidLengthProvider - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - var elementCount int - var count int - for _, integer := range dAtA[iNdEx:postIndex] { - if integer < 128 { - count++ - } - } - elementCount = count - if elementCount != 0 && len(m.UnbondingOpIds) == 0 { - m.UnbondingOpIds = make([]uint64, 0, elementCount) - } - for iNdEx < postIndex { - var v uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProvider - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.UnbondingOpIds = append(m.UnbondingOpIds, v) - } - } else { - return fmt.Errorf("proto: wrong wireType = %d for field UnbondingOpIds", wireType) + if msglen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.List = append(m.List, types3.ValidatorSetChangePacketData{}) + if err := m.List[len(m.List)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipProvider(dAtA[iNdEx:]) @@ -6181,7 +5534,7 @@ func (m *VscUnbondingOps) Unmarshal(dAtA []byte) error { } return nil } -func (m *UnbondingOp) Unmarshal(dAtA []byte) error { +func (m *KeyAssignmentReplacement) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -6204,17 +5557,17 @@ func (m *UnbondingOp) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: UnbondingOp: wiretype end group for non-group") + return fmt.Errorf("proto: KeyAssignmentReplacement: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: UnbondingOp: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: KeyAssignmentReplacement: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProviderAddr", wireType) } - m.Id = 0 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowProvider @@ -6224,16 +5577,31 @@ func (m *UnbondingOp) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Id |= uint64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } + if byteLen < 0 { + return ErrInvalidLengthProvider + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthProvider + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProviderAddr = append(m.ProviderAddr[:0], dAtA[iNdEx:postIndex]...) + if m.ProviderAddr == nil { + m.ProviderAddr = []byte{} + } + iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field UnbondingConsumerChains", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field PrevCKey", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowProvider @@ -6243,24 +5611,47 @@ func (m *UnbondingOp) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthProvider } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthProvider } if postIndex > l { return io.ErrUnexpectedEOF } - m.UnbondingConsumerChains = append(m.UnbondingConsumerChains, string(dAtA[iNdEx:postIndex])) + if m.PrevCKey == nil { + m.PrevCKey = &crypto.PublicKey{} + } + if err := m.PrevCKey.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 ErrIntOverflowProvider + } + 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 := skipProvider(dAtA[iNdEx:]) @@ -6282,7 +5673,7 @@ func (m *UnbondingOp) Unmarshal(dAtA []byte) error { } return nil } -func (m *InitTimeoutTimestamp) Unmarshal(dAtA []byte) error { +func (m *ValidatorConsumerPubKey) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -6305,10 +5696,10 @@ func (m *InitTimeoutTimestamp) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: InitTimeoutTimestamp: wiretype end group for non-group") + return fmt.Errorf("proto: ValidatorConsumerPubKey: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: InitTimeoutTimestamp: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ValidatorConsumerPubKey: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -6344,10 +5735,10 @@ func (m *InitTimeoutTimestamp) Unmarshal(dAtA []byte) error { m.ChainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProviderAddr", wireType) } - m.Timestamp = 0 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowProvider @@ -6357,687 +5748,19 @@ func (m *InitTimeoutTimestamp) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Timestamp |= uint64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - default: - iNdEx = preIndex - skippy, err := skipProvider(dAtA[iNdEx:]) - if err != nil { - return err + if byteLen < 0 { + return ErrInvalidLengthProvider } - if (skippy < 0) || (iNdEx+skippy) < 0 { + postIndex := iNdEx + byteLen + if postIndex < 0 { return ErrInvalidLengthProvider } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *VscSendTimestamp) 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: VscSendTimestamp: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: VscSendTimestamp: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field VscId", wireType) - } - m.VscId = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProvider - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.VscId |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", 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 - } - if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(&m.Timestamp, 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 (m *ValidatorSetChangePackets) 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: ValidatorSetChangePackets: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ValidatorSetChangePackets: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field List", 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.List = append(m.List, types3.ValidatorSetChangePacketData{}) - if err := m.List[len(m.List)-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 (m *MaturedUnbondingOps) 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: MaturedUnbondingOps: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: MaturedUnbondingOps: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType == 0 { - var v uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProvider - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.Ids = append(m.Ids, v) - } else if wireType == 2 { - var packedLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProvider - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - packedLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if packedLen < 0 { - return ErrInvalidLengthProvider - } - postIndex := iNdEx + packedLen - if postIndex < 0 { - return ErrInvalidLengthProvider - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - var elementCount int - var count int - for _, integer := range dAtA[iNdEx:postIndex] { - if integer < 128 { - count++ - } - } - elementCount = count - if elementCount != 0 && len(m.Ids) == 0 { - m.Ids = make([]uint64, 0, elementCount) - } - for iNdEx < postIndex { - var v uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProvider - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.Ids = append(m.Ids, v) - } - } else { - return fmt.Errorf("proto: wrong wireType = %d for field Ids", wireType) - } - 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 *ExportedVscSendTimestamp) 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: ExportedVscSendTimestamp: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ExportedVscSendTimestamp: 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 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 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field VscSendTimestamps", 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.VscSendTimestamps = append(m.VscSendTimestamps, VscSendTimestamp{}) - if err := m.VscSendTimestamps[len(m.VscSendTimestamps)-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 (m *KeyAssignmentReplacement) 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: KeyAssignmentReplacement: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: KeyAssignmentReplacement: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ProviderAddr", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProvider - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthProvider - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthProvider - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ProviderAddr = append(m.ProviderAddr[:0], dAtA[iNdEx:postIndex]...) - if m.ProviderAddr == nil { - m.ProviderAddr = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PrevCKey", 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 - } - if m.PrevCKey == nil { - m.PrevCKey = &crypto.PublicKey{} - } - if err := m.PrevCKey.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 ErrIntOverflowProvider - } - 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 := 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 *ValidatorConsumerPubKey) 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: ValidatorConsumerPubKey: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ValidatorConsumerPubKey: 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 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 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ProviderAddr", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProvider - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthProvider - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthProvider - } - if postIndex > l { + if postIndex > l { return io.ErrUnexpectedEOF } m.ProviderAddr = append(m.ProviderAddr[:0], dAtA[iNdEx:postIndex]...) @@ -7252,7 +5975,7 @@ func (m *ValidatorByConsumerAddr) Unmarshal(dAtA []byte) error { } return nil } -func (m *ConsumerAddrsToPrune) Unmarshal(dAtA []byte) error { +func (m *ConsumerAddrsToPruneV2) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -7275,10 +5998,10 @@ func (m *ConsumerAddrsToPrune) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ConsumerAddrsToPrune: wiretype end group for non-group") + return fmt.Errorf("proto: ConsumerAddrsToPruneV2: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ConsumerAddrsToPrune: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ConsumerAddrsToPruneV2: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -7314,10 +6037,10 @@ func (m *ConsumerAddrsToPrune) Unmarshal(dAtA []byte) error { m.ChainId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field VscId", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PruneTs", wireType) } - m.VscId = 0 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowProvider @@ -7327,11 +6050,25 @@ func (m *ConsumerAddrsToPrune) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.VscId |= uint64(b&0x7F) << shift + 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 + } + if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(&m.PruneTs, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ConsumerAddrs", wireType) @@ -7389,7 +6126,7 @@ func (m *ConsumerAddrsToPrune) Unmarshal(dAtA []byte) error { } return nil } -func (m *ConsumerValidator) Unmarshal(dAtA []byte) error { +func (m *ConsensusValidator) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -7412,10 +6149,10 @@ func (m *ConsumerValidator) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ConsumerValidator: wiretype end group for non-group") + return fmt.Errorf("proto: ConsensusValidator: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ConsumerValidator: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ConsensusValidator: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -7473,7 +6210,7 @@ func (m *ConsumerValidator) Unmarshal(dAtA []byte) error { } case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ConsumerPublicKey", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -7500,10 +6237,10 @@ func (m *ConsumerValidator) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.ConsumerPublicKey == nil { - m.ConsumerPublicKey = &crypto.PublicKey{} + if m.PublicKey == nil { + m.PublicKey = &crypto.PublicKey{} } - if err := m.ConsumerPublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.PublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/x/ccv/provider/types/query.pb.go b/x/ccv/provider/types/query.pb.go index 84e610776e..10a78b00c7 100644 --- a/x/ccv/provider/types/query.pb.go +++ b/x/ccv/provider/types/query.pb.go @@ -391,6 +391,10 @@ type Chain struct { 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"` + // Corresponds to the minimal amount of (provider chain) stake required to validate on the consumer chain. + MinStake uint64 `protobuf:"varint,9,opt,name=min_stake,json=minStake,proto3" json:"min_stake,omitempty"` + // Corresponds to whether inactive validators are allowed to validate the consumer chain. + AllowInactiveVals bool `protobuf:"varint,10,opt,name=allow_inactive_vals,json=allowInactiveVals,proto3" json:"allow_inactive_vals,omitempty"` } func (m *Chain) Reset() { *m = Chain{} } @@ -482,6 +486,20 @@ func (m *Chain) GetDenylist() []string { return nil } +func (m *Chain) GetMinStake() uint64 { + if m != nil { + return m.MinStake + } + return 0 +} + +func (m *Chain) GetAllowInactiveVals() bool { + if m != nil { + return m.AllowInactiveVals + } + return false +} + 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"` @@ -1360,6 +1378,8 @@ type QueryConsumerValidatorsValidator struct { 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"` + // The rate to charge delegators on the consumer chain, as a fraction + Rate cosmossdk_io_math.LegacyDec `protobuf:"bytes,4,opt,name=rate,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"rate"` } func (m *QueryConsumerValidatorsValidator) Reset() { *m = QueryConsumerValidatorsValidator{} } @@ -1656,22 +1676,21 @@ func (m *QueryValidatorConsumerCommissionRateResponse) XXX_DiscardUnknown() { 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"` +type QueryBlocksUntilNextEpochRequest struct { } -func (m *QueryOldestUnconfirmedVscRequest) Reset() { *m = QueryOldestUnconfirmedVscRequest{} } -func (m *QueryOldestUnconfirmedVscRequest) String() string { return proto.CompactTextString(m) } -func (*QueryOldestUnconfirmedVscRequest) ProtoMessage() {} -func (*QueryOldestUnconfirmedVscRequest) Descriptor() ([]byte, []int) { +func (m *QueryBlocksUntilNextEpochRequest) Reset() { *m = QueryBlocksUntilNextEpochRequest{} } +func (m *QueryBlocksUntilNextEpochRequest) String() string { return proto.CompactTextString(m) } +func (*QueryBlocksUntilNextEpochRequest) ProtoMessage() {} +func (*QueryBlocksUntilNextEpochRequest) Descriptor() ([]byte, []int) { return fileDescriptor_422512d7b7586cd7, []int{34} } -func (m *QueryOldestUnconfirmedVscRequest) XXX_Unmarshal(b []byte) error { +func (m *QueryBlocksUntilNextEpochRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *QueryOldestUnconfirmedVscRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *QueryBlocksUntilNextEpochRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_QueryOldestUnconfirmedVscRequest.Marshal(b, m, deterministic) + return xxx_messageInfo_QueryBlocksUntilNextEpochRequest.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -1681,41 +1700,35 @@ func (m *QueryOldestUnconfirmedVscRequest) XXX_Marshal(b []byte, deterministic b return b[:n], nil } } -func (m *QueryOldestUnconfirmedVscRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryOldestUnconfirmedVscRequest.Merge(m, src) +func (m *QueryBlocksUntilNextEpochRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBlocksUntilNextEpochRequest.Merge(m, src) } -func (m *QueryOldestUnconfirmedVscRequest) XXX_Size() int { +func (m *QueryBlocksUntilNextEpochRequest) XXX_Size() int { return m.Size() } -func (m *QueryOldestUnconfirmedVscRequest) XXX_DiscardUnknown() { - xxx_messageInfo_QueryOldestUnconfirmedVscRequest.DiscardUnknown(m) +func (m *QueryBlocksUntilNextEpochRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBlocksUntilNextEpochRequest.DiscardUnknown(m) } -var xxx_messageInfo_QueryOldestUnconfirmedVscRequest proto.InternalMessageInfo +var xxx_messageInfo_QueryBlocksUntilNextEpochRequest proto.InternalMessageInfo -func (m *QueryOldestUnconfirmedVscRequest) GetChainId() string { - if m != nil { - return m.ChainId - } - return "" -} - -type QueryOldestUnconfirmedVscResponse struct { - VscSendTimestamp VscSendTimestamp `protobuf:"bytes,1,opt,name=vsc_send_timestamp,json=vscSendTimestamp,proto3" json:"vsc_send_timestamp"` +type QueryBlocksUntilNextEpochResponse struct { + // The number of blocks until the next epoch starts + BlocksUntilNextEpoch uint64 `protobuf:"varint,1,opt,name=blocks_until_next_epoch,json=blocksUntilNextEpoch,proto3" json:"blocks_until_next_epoch,omitempty"` } -func (m *QueryOldestUnconfirmedVscResponse) Reset() { *m = QueryOldestUnconfirmedVscResponse{} } -func (m *QueryOldestUnconfirmedVscResponse) String() string { return proto.CompactTextString(m) } -func (*QueryOldestUnconfirmedVscResponse) ProtoMessage() {} -func (*QueryOldestUnconfirmedVscResponse) Descriptor() ([]byte, []int) { +func (m *QueryBlocksUntilNextEpochResponse) Reset() { *m = QueryBlocksUntilNextEpochResponse{} } +func (m *QueryBlocksUntilNextEpochResponse) String() string { return proto.CompactTextString(m) } +func (*QueryBlocksUntilNextEpochResponse) ProtoMessage() {} +func (*QueryBlocksUntilNextEpochResponse) Descriptor() ([]byte, []int) { return fileDescriptor_422512d7b7586cd7, []int{35} } -func (m *QueryOldestUnconfirmedVscResponse) XXX_Unmarshal(b []byte) error { +func (m *QueryBlocksUntilNextEpochResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *QueryOldestUnconfirmedVscResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *QueryBlocksUntilNextEpochResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_QueryOldestUnconfirmedVscResponse.Marshal(b, m, deterministic) + return xxx_messageInfo_QueryBlocksUntilNextEpochResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -1725,23 +1738,23 @@ func (m *QueryOldestUnconfirmedVscResponse) XXX_Marshal(b []byte, deterministic return b[:n], nil } } -func (m *QueryOldestUnconfirmedVscResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryOldestUnconfirmedVscResponse.Merge(m, src) +func (m *QueryBlocksUntilNextEpochResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBlocksUntilNextEpochResponse.Merge(m, src) } -func (m *QueryOldestUnconfirmedVscResponse) XXX_Size() int { +func (m *QueryBlocksUntilNextEpochResponse) XXX_Size() int { return m.Size() } -func (m *QueryOldestUnconfirmedVscResponse) XXX_DiscardUnknown() { - xxx_messageInfo_QueryOldestUnconfirmedVscResponse.DiscardUnknown(m) +func (m *QueryBlocksUntilNextEpochResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBlocksUntilNextEpochResponse.DiscardUnknown(m) } -var xxx_messageInfo_QueryOldestUnconfirmedVscResponse proto.InternalMessageInfo +var xxx_messageInfo_QueryBlocksUntilNextEpochResponse proto.InternalMessageInfo -func (m *QueryOldestUnconfirmedVscResponse) GetVscSendTimestamp() VscSendTimestamp { +func (m *QueryBlocksUntilNextEpochResponse) GetBlocksUntilNextEpoch() uint64 { if m != nil { - return m.VscSendTimestamp + return m.BlocksUntilNextEpoch } - return VscSendTimestamp{} + return 0 } func init() { @@ -1779,8 +1792,8 @@ func init() { 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") + proto.RegisterType((*QueryBlocksUntilNextEpochRequest)(nil), "interchain_security.ccv.provider.v1.QueryBlocksUntilNextEpochRequest") + proto.RegisterType((*QueryBlocksUntilNextEpochResponse)(nil), "interchain_security.ccv.provider.v1.QueryBlocksUntilNextEpochResponse") } func init() { @@ -1788,133 +1801,136 @@ func init() { } var fileDescriptor_422512d7b7586cd7 = []byte{ - // 2015 bytes of a gzipped FileDescriptorProto + // 2049 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, 0x42, 0xf7, - 0x43, 0x96, 0x5d, 0x52, 0x92, 0x61, 0xc4, 0xb1, 0xab, 0xc8, 0x5a, 0xc9, 0x76, 0x16, 0x76, 0x62, - 0x85, 0x96, 0xd5, 0xc2, 0x2d, 0x4a, 0x8f, 0xc9, 0xc9, 0x8a, 0x30, 0x97, 0x43, 0x71, 0xb8, 0xeb, - 0x2c, 0x8c, 0x1c, 0xd2, 0x43, 0x9b, 0x53, 0x11, 0xf4, 0x03, 0xe8, 0x31, 0x97, 0x1e, 0x7b, 0xe9, - 0xa1, 0xc8, 0x9f, 0x90, 0x5b, 0x53, 0xe4, 0x52, 0xf4, 0xe0, 0x16, 0x72, 0x0f, 0x45, 0x0f, 0x45, - 0x61, 0x14, 0xe8, 0xa9, 0x40, 0xc1, 0xe1, 0xf0, 0x6b, 0x97, 0xbb, 0x4b, 0xee, 0x2a, 0xb7, 0xe5, - 0xcc, 0xbc, 0xdf, 0xbc, 0xf7, 0xe6, 0xbd, 0x37, 0xef, 0x37, 0x0b, 0x14, 0xd3, 0xf6, 0xb0, 0xab, - 0x1f, 0x20, 0xd3, 0xd6, 0x28, 0xd6, 0x9b, 0xae, 0xe9, 0xb5, 0x15, 0x5d, 0x6f, 0x29, 0x8e, 0x4b, - 0x5a, 0xa6, 0x81, 0x5d, 0xa5, 0xb5, 0xa6, 0x1c, 0x36, 0xb1, 0xdb, 0x96, 0x1d, 0x97, 0x78, 0x04, - 0x9e, 0xcb, 0x10, 0x90, 0x75, 0xbd, 0x25, 0x87, 0x02, 0x72, 0x6b, 0x4d, 0x5c, 0xac, 0x13, 0x52, - 0xb7, 0xb0, 0x82, 0x1c, 0x53, 0x41, 0xb6, 0x4d, 0x3c, 0xe4, 0x99, 0xc4, 0xa6, 0x01, 0x84, 0x38, - 0x57, 0x27, 0x75, 0xc2, 0x7e, 0x2a, 0xfe, 0x2f, 0x3e, 0x5a, 0xe1, 0x32, 0xec, 0xeb, 0x51, 0xf3, - 0x03, 0xc5, 0x33, 0x1b, 0x98, 0x7a, 0xa8, 0xe1, 0xf0, 0x05, 0xeb, 0x79, 0x54, 0x8d, 0xb4, 0x08, - 0x64, 0x56, 0x7b, 0xc9, 0xb4, 0xd6, 0x14, 0x7a, 0x80, 0x5c, 0x6c, 0x68, 0x3a, 0xb1, 0x69, 0xb3, - 0x11, 0x49, 0x7c, 0xab, 0x8f, 0xc4, 0x13, 0xd3, 0xc5, 0x7c, 0xd9, 0xa2, 0x87, 0x6d, 0x03, 0xbb, - 0x0d, 0xd3, 0xf6, 0x14, 0xdd, 0x6d, 0x3b, 0x1e, 0x51, 0x1e, 0xe3, 0x76, 0x68, 0xe1, 0x59, 0x9d, - 0xd0, 0x06, 0xa1, 0x5a, 0x60, 0x64, 0xf0, 0x11, 0x4c, 0x49, 0x57, 0xc0, 0xc2, 0xfb, 0xbe, 0x3b, - 0xb7, 0xf9, 0xb6, 0xb7, 0xb0, 0x8d, 0xa9, 0x49, 0x55, 0x7c, 0xd8, 0xc4, 0xd4, 0x83, 0x67, 0xc1, - 0x74, 0xb0, 0xb7, 0x69, 0xcc, 0x0b, 0x4b, 0xc2, 0xf2, 0x8c, 0xfa, 0x12, 0xfb, 0xae, 0x19, 0xd2, - 0x53, 0xb0, 0x98, 0x2d, 0x49, 0x1d, 0x62, 0x53, 0x0c, 0x7f, 0x08, 0x4e, 0xd4, 0x83, 0x21, 0x8d, - 0x7a, 0xc8, 0xc3, 0x4c, 0x7e, 0x76, 0x7d, 0x55, 0xee, 0x75, 0x62, 0xad, 0x35, 0xb9, 0x03, 0xeb, - 0x9e, 0x2f, 0x57, 0x9d, 0xf8, 0xe2, 0x59, 0x65, 0x4c, 0x7d, 0xb9, 0x9e, 0x18, 0x93, 0x16, 0x81, - 0x98, 0xda, 0x7c, 0xdb, 0x87, 0x0b, 0xb5, 0x96, 0x50, 0x87, 0x51, 0xe1, 0x2c, 0xd7, 0xac, 0x0a, - 0xa6, 0xd8, 0xf6, 0x74, 0x5e, 0x58, 0x1a, 0x5f, 0x9e, 0x5d, 0x5f, 0x91, 0x73, 0x04, 0x91, 0xcc, - 0x40, 0x54, 0x2e, 0x29, 0x9d, 0x07, 0xdf, 0xe9, 0xde, 0xe2, 0x9e, 0x87, 0x5c, 0x6f, 0xd7, 0x25, - 0x0e, 0xa1, 0xc8, 0x8a, 0xb4, 0xf9, 0x44, 0x00, 0xcb, 0x83, 0xd7, 0x72, 0xdd, 0x7e, 0x04, 0x66, - 0x9c, 0x70, 0x90, 0x7b, 0xec, 0xed, 0x7c, 0xea, 0x71, 0xf0, 0x2d, 0xc3, 0x30, 0xfd, 0xe8, 0x8e, - 0xa1, 0x63, 0x40, 0x69, 0x19, 0x7c, 0x3b, 0x4b, 0x13, 0xe2, 0x74, 0x29, 0xfd, 0x53, 0x21, 0xdb, - 0xc0, 0xd4, 0xd2, 0xe8, 0xa4, 0xbb, 0x74, 0xde, 0x28, 0xa4, 0xb3, 0x8a, 0x1b, 0xa4, 0x85, 0xac, - 0x4c, 0x95, 0x7f, 0x53, 0x02, 0x93, 0x6c, 0xef, 0x3e, 0xb1, 0x08, 0x17, 0xc0, 0x8c, 0x6e, 0x99, - 0xd8, 0xf6, 0xfc, 0xb9, 0x12, 0x9b, 0x9b, 0x0e, 0x06, 0x6a, 0x06, 0x3c, 0x03, 0x26, 0x3d, 0xe2, - 0x68, 0xef, 0xcd, 0x8f, 0x2f, 0x09, 0xcb, 0x27, 0xd4, 0x09, 0x8f, 0x38, 0xef, 0xc1, 0x15, 0x00, - 0x1b, 0xa6, 0xad, 0x39, 0xe4, 0x09, 0x76, 0x35, 0xd3, 0xd6, 0x82, 0x15, 0x13, 0x4b, 0xc2, 0xf2, - 0xb8, 0x7a, 0xb2, 0x61, 0xda, 0xbb, 0xfe, 0x44, 0xcd, 0xde, 0xf3, 0xd7, 0xae, 0x82, 0xb9, 0x16, - 0xb2, 0x4c, 0x03, 0x79, 0xc4, 0xa5, 0x5c, 0x44, 0x47, 0xce, 0xfc, 0x24, 0xc3, 0x83, 0xf1, 0x1c, - 0x13, 0xda, 0x46, 0x0e, 0x5c, 0x01, 0xaf, 0x44, 0xa3, 0x1a, 0xc5, 0x1e, 0x5b, 0x3e, 0xc5, 0x96, - 0x9f, 0x8a, 0x26, 0xee, 0x61, 0xcf, 0x5f, 0xbb, 0x08, 0x66, 0x90, 0x65, 0x91, 0x27, 0x96, 0x49, - 0xbd, 0xf9, 0x97, 0x96, 0xc6, 0x97, 0x67, 0xd4, 0x78, 0x00, 0x8a, 0x60, 0xda, 0xc0, 0x76, 0x9b, - 0x4d, 0x4e, 0xb3, 0xc9, 0xe8, 0x5b, 0xfa, 0x99, 0x00, 0xde, 0x60, 0x67, 0xb4, 0x1f, 0x42, 0x26, - 0x82, 0xc0, 0x1d, 0x9c, 0xc2, 0x70, 0x03, 0x9c, 0x0e, 0x8f, 0x43, 0x43, 0x86, 0xe1, 0x62, 0x4a, - 0x03, 0xef, 0x55, 0xe1, 0x8b, 0x67, 0x95, 0x93, 0x6d, 0xd4, 0xb0, 0xae, 0x4a, 0x7c, 0x42, 0x52, - 0x4f, 0x85, 0x6b, 0xb7, 0x82, 0x91, 0xab, 0xd3, 0x9f, 0x7c, 0x56, 0x19, 0xfb, 0xc7, 0x67, 0x95, - 0x31, 0xe9, 0x2e, 0x90, 0xfa, 0x29, 0xc2, 0xe3, 0xe4, 0x3c, 0x38, 0x1d, 0x56, 0xb7, 0x68, 0xbb, - 0x40, 0xa3, 0x53, 0x7a, 0x62, 0xbd, 0xbf, 0x59, 0xb7, 0x69, 0xbb, 0x89, 0xcd, 0xf3, 0x99, 0xd6, - 0xb5, 0x57, 0x1f, 0xd3, 0x3a, 0xf6, 0xef, 0x67, 0x5a, 0x5a, 0x91, 0xd8, 0xb4, 0x2e, 0x4f, 0x72, - 0xd3, 0x3a, 0xbc, 0x26, 0x2d, 0x80, 0xb3, 0x0c, 0x70, 0xef, 0xc0, 0x25, 0x9e, 0x67, 0x61, 0x56, - 0xd0, 0xc2, 0xb4, 0xfb, 0x93, 0xc0, 0x0b, 0x5b, 0xc7, 0x2c, 0xdf, 0xa6, 0x02, 0x66, 0xa9, 0x85, - 0xe8, 0x81, 0xd6, 0xc0, 0x1e, 0x76, 0xd9, 0x0e, 0xe3, 0x2a, 0x60, 0x43, 0xef, 0xfa, 0x23, 0x70, - 0x1d, 0x7c, 0x23, 0xb1, 0x40, 0x63, 0x71, 0x84, 0x6c, 0x1d, 0x33, 0xdb, 0xc7, 0xd5, 0x33, 0xf1, - 0xd2, 0xad, 0x70, 0x0a, 0xfe, 0x18, 0xcc, 0xdb, 0xf8, 0x43, 0x4f, 0x73, 0xb1, 0x63, 0x61, 0xdb, - 0xa4, 0x07, 0x9a, 0x8e, 0x6c, 0xc3, 0x37, 0x16, 0xb3, 0x94, 0x99, 0x5d, 0x17, 0xe5, 0xe0, 0x32, - 0x94, 0xc3, 0xcb, 0x50, 0xde, 0x0b, 0x2f, 0xc3, 0xea, 0xb4, 0x5f, 0x9d, 0x3f, 0xfd, 0x6b, 0x45, - 0x50, 0x5f, 0xf5, 0x51, 0xd4, 0x10, 0x64, 0x3b, 0xc4, 0x90, 0x2e, 0x82, 0x15, 0x66, 0x92, 0x8a, - 0xeb, 0x26, 0xf5, 0xb0, 0x8b, 0x8d, 0x38, 0xef, 0x9f, 0x20, 0xd7, 0xd8, 0xc1, 0x36, 0x69, 0x44, - 0x85, 0xe7, 0x06, 0xb8, 0x90, 0x6b, 0x35, 0xf7, 0xc8, 0xab, 0x60, 0xca, 0x60, 0x23, 0xac, 0x96, - 0xcf, 0xa8, 0xfc, 0x4b, 0x2a, 0xf3, 0xdb, 0x29, 0xa8, 0x29, 0xd8, 0x60, 0x25, 0xa4, 0xb6, 0x13, - 0x6d, 0xf3, 0xb1, 0x00, 0x5e, 0xef, 0xb1, 0x80, 0x23, 0x3f, 0x04, 0x27, 0x9d, 0xe4, 0x5c, 0x78, - 0x5b, 0xac, 0xe7, 0x2a, 0x6d, 0x29, 0x58, 0x7e, 0x85, 0x75, 0xe0, 0x49, 0x35, 0x70, 0x22, 0xb5, - 0x0c, 0xce, 0x03, 0x1e, 0xbf, 0x3b, 0xe9, 0x70, 0xde, 0x81, 0x65, 0x00, 0xc2, 0x92, 0x58, 0xdb, - 0x61, 0x87, 0x39, 0xa1, 0x26, 0x46, 0xa4, 0x3b, 0x40, 0x61, 0xd6, 0x6c, 0x59, 0xd6, 0x2e, 0x32, - 0x5d, 0xba, 0x8f, 0xac, 0x6d, 0x62, 0xfb, 0x21, 0x57, 0x4d, 0x57, 0xf0, 0xda, 0x4e, 0x8e, 0xab, - 0xfd, 0xb7, 0x02, 0x58, 0xcd, 0x0f, 0xc7, 0xfd, 0x75, 0x08, 0x5e, 0x71, 0x90, 0xe9, 0x6a, 0x2d, - 0x64, 0xf9, 0x4d, 0x0c, 0x4b, 0x03, 0xee, 0xb2, 0x9b, 0xf9, 0x5c, 0x86, 0x4c, 0x37, 0xde, 0x28, - 0x4a, 0x33, 0x3b, 0x0e, 0x80, 0x93, 0x4e, 0x6a, 0x89, 0xf4, 0x1f, 0x01, 0xbc, 0x31, 0x50, 0x0a, - 0xde, 0xec, 0x95, 0x9b, 0xd5, 0x85, 0x17, 0xcf, 0x2a, 0xaf, 0x05, 0xa5, 0xa0, 0x73, 0x45, 0x77, - 0xb9, 0xf3, 0x71, 0x7a, 0x94, 0x94, 0x04, 0x4e, 0xe7, 0x8a, 0xee, 0xda, 0x02, 0x37, 0xc1, 0xcb, - 0xd1, 0xaa, 0xc7, 0xb8, 0xcd, 0x73, 0x6c, 0x51, 0x8e, 0x5b, 0x38, 0x39, 0x68, 0xe1, 0xe4, 0xdd, - 0xe6, 0x23, 0xcb, 0xd4, 0x6f, 0xe3, 0xb6, 0x3a, 0x1b, 0x4a, 0xdc, 0xc6, 0x6d, 0x69, 0x0e, 0xc0, - 0x20, 0x74, 0x91, 0x8b, 0xe2, 0xc4, 0x79, 0x08, 0xce, 0xa4, 0x46, 0xf9, 0xb1, 0xd4, 0xc0, 0x94, - 0xc3, 0x46, 0xf8, 0xcd, 0x7c, 0x21, 0xe7, 0x59, 0xf8, 0x22, 0x3c, 0x6e, 0x39, 0x80, 0x74, 0x8b, - 0x27, 0x72, 0x2a, 0x02, 0xee, 0x3a, 0x1e, 0x36, 0x6a, 0x76, 0x54, 0x1e, 0xf3, 0xb4, 0x8e, 0x87, - 0x3c, 0xc7, 0x07, 0x01, 0x45, 0xfd, 0xda, 0xeb, 0xc9, 0xfb, 0xb7, 0xe3, 0xa4, 0x70, 0x98, 0xfa, - 0x0b, 0x89, 0x8b, 0x38, 0x7d, 0x74, 0x98, 0x4a, 0xd7, 0x40, 0x39, 0xb5, 0x65, 0x21, 0x7d, 0x3f, - 0x17, 0xc0, 0x52, 0x0f, 0xe9, 0xe8, 0x57, 0xe6, 0x65, 0x2a, 0xe4, 0xbe, 0x4c, 0xbb, 0xa2, 0xa2, - 0x54, 0x30, 0x2a, 0xe0, 0x1c, 0x98, 0x64, 0xad, 0x09, 0x8b, 0xa7, 0x71, 0x35, 0xf8, 0xf0, 0x9b, - 0xcf, 0x4a, 0x4f, 0xc3, 0xb9, 0x7f, 0x31, 0x00, 0xb1, 0xeb, 0x78, 0xca, 0xde, 0xc8, 0x15, 0x26, - 0x83, 0x9c, 0xa2, 0x26, 0x80, 0xa5, 0x43, 0x5e, 0x54, 0xd2, 0x5d, 0x79, 0xb4, 0xf6, 0x1d, 0x44, - 0xf7, 0x08, 0xff, 0x0a, 0xef, 0xc3, 0x11, 0x9d, 0x2a, 0x21, 0xb0, 0x56, 0x60, 0x4b, 0xee, 0x8e, - 0x8b, 0x00, 0x46, 0x27, 0x11, 0x46, 0x44, 0x18, 0x63, 0x51, 0x05, 0x08, 0xaa, 0x9f, 0xc1, 0x3a, - 0x95, 0x0b, 0xd9, 0xbd, 0xcf, 0x36, 0x69, 0x34, 0x4c, 0x4a, 0x4d, 0x62, 0xab, 0x09, 0x8b, 0xbe, - 0xb6, 0x76, 0x4c, 0xaa, 0x83, 0x8b, 0xf9, 0x14, 0xe1, 0x76, 0xbe, 0x09, 0x26, 0xdc, 0x90, 0x97, - 0xcd, 0x54, 0xcf, 0xf9, 0xa9, 0xfe, 0x97, 0x67, 0x95, 0x85, 0x80, 0x1e, 0x52, 0xe3, 0xb1, 0x6c, - 0x12, 0xa5, 0x81, 0xbc, 0x03, 0xf9, 0x0e, 0xae, 0x23, 0xbd, 0xbd, 0x83, 0x75, 0x95, 0x09, 0x48, - 0x1b, 0x3c, 0x1b, 0xee, 0x5a, 0x06, 0xa6, 0xde, 0x7d, 0x5b, 0x27, 0xf6, 0x07, 0xa6, 0xdb, 0xc0, - 0xc6, 0x3e, 0xd5, 0x73, 0x64, 0xd3, 0xcf, 0xc3, 0xde, 0x2e, 0x5b, 0x9e, 0x6b, 0x67, 0x02, 0xd8, - 0xa2, 0xba, 0x46, 0xb1, 0x6d, 0x68, 0x11, 0xf5, 0xe6, 0x35, 0xec, 0x72, 0xae, 0xe0, 0xdc, 0xa7, - 0xfa, 0x3d, 0x6c, 0x1b, 0x71, 0xab, 0x12, 0x54, 0xb3, 0xd3, 0xad, 0x8e, 0xf1, 0xf5, 0xcf, 0x2b, - 0x60, 0x92, 0x29, 0x04, 0x8f, 0x04, 0x30, 0x97, 0x45, 0x6a, 0xe1, 0xf5, 0xe2, 0xe9, 0x90, 0x66, - 0xd2, 0xe2, 0xd6, 0x08, 0x08, 0x81, 0x4b, 0xa4, 0x1b, 0x3f, 0xf9, 0xea, 0xef, 0xbf, 0x2c, 0x6d, - 0xc2, 0x8d, 0xc1, 0xaf, 0x24, 0x51, 0x00, 0x73, 0xd6, 0xac, 0x3c, 0x0d, 0x4f, 0xe3, 0x23, 0xf8, - 0x95, 0xc0, 0x6f, 0x8a, 0x74, 0x56, 0xc0, 0xcd, 0xe2, 0x1a, 0xa6, 0x68, 0xb7, 0x78, 0x7d, 0x78, - 0x00, 0x6e, 0xe1, 0x5b, 0xcc, 0xc2, 0x4b, 0x70, 0xad, 0x80, 0x85, 0x01, 0x21, 0x87, 0x1f, 0x97, - 0xc0, 0x7c, 0x0f, 0x96, 0x4d, 0xe1, 0x9d, 0x21, 0x35, 0xcb, 0x24, 0xf4, 0xe2, 0xbb, 0xc7, 0x84, - 0xc6, 0x8d, 0x7e, 0x87, 0x19, 0x5d, 0x85, 0xd7, 0x8b, 0x1a, 0xad, 0x51, 0x1f, 0x50, 0x8b, 0xb8, - 0x32, 0xfc, 0x9f, 0x00, 0x5e, 0xcb, 0x26, 0xed, 0x14, 0xde, 0x1e, 0x5a, 0xe9, 0xee, 0xd7, 0x01, - 0xf1, 0xce, 0xf1, 0x80, 0x71, 0x07, 0xdc, 0x62, 0x0e, 0xd8, 0x82, 0x9b, 0x43, 0x38, 0x80, 0x38, - 0x09, 0xfb, 0xff, 0x1d, 0xb2, 0xa7, 0x4c, 0x1e, 0x0a, 0x6f, 0xe6, 0xd7, 0xba, 0x1f, 0xa3, 0x16, - 0x6f, 0x8d, 0x8c, 0xc3, 0x0d, 0xdf, 0x62, 0x86, 0x5f, 0x83, 0x6f, 0xe5, 0x78, 0xf6, 0x8c, 0x9e, - 0x13, 0x52, 0x1d, 0x66, 0x86, 0xc9, 0xc9, 0xde, 0x67, 0x28, 0x93, 0x33, 0x98, 0xf6, 0x50, 0x26, - 0x67, 0x11, 0xe5, 0xe1, 0x4c, 0x4e, 0xdd, 0x8a, 0xf0, 0x8f, 0x02, 0xef, 0x7f, 0x53, 0x1c, 0x19, - 0xbe, 0x9d, 0x5f, 0xc5, 0x2c, 0xea, 0x2d, 0x6e, 0x0e, 0x2d, 0xcf, 0x4d, 0xbb, 0xc2, 0x4c, 0x5b, - 0x87, 0xab, 0x83, 0x4d, 0xf3, 0x38, 0x40, 0xf0, 0x32, 0x0a, 0x7f, 0x5d, 0x02, 0xe7, 0x72, 0x90, - 0x5e, 0x78, 0x37, 0xbf, 0x8a, 0xb9, 0xc8, 0xb6, 0xb8, 0x7b, 0x7c, 0x80, 0xdc, 0x09, 0xb7, 0x99, - 0x13, 0x6e, 0xc0, 0xed, 0xc1, 0x4e, 0x70, 0x23, 0xc4, 0x38, 0xa6, 0x5d, 0x86, 0xa9, 0x05, 0x24, - 0x1e, 0xfe, 0xb3, 0x8b, 0xa4, 0xa7, 0xb9, 0x27, 0x85, 0x05, 0x6e, 0xd5, 0x1e, 0x2f, 0x01, 0x62, - 0x75, 0x14, 0x08, 0x6e, 0x75, 0x95, 0x59, 0xfd, 0x3d, 0x78, 0x75, 0xb0, 0xd5, 0xe1, 0x1b, 0x80, - 0xd6, 0x79, 0x81, 0xfd, 0xaa, 0xc4, 0x9f, 0x89, 0x73, 0x90, 0x6e, 0xb8, 0x97, 0x5f, 0xe9, 0xfc, - 0x4f, 0x02, 0xe2, 0xfd, 0x63, 0x46, 0xe5, 0xde, 0xb9, 0xc6, 0xbc, 0x73, 0x19, 0x5e, 0x2a, 0x5c, - 0xdf, 0x4d, 0x03, 0xfe, 0x5e, 0x00, 0xb3, 0x09, 0x5e, 0x0b, 0xdf, 0x2c, 0x70, 0x5c, 0x49, 0x7e, - 0x2c, 0x5e, 0x29, 0x2e, 0xc8, 0xf5, 0x5f, 0x65, 0xfa, 0xaf, 0xc0, 0xe5, 0x1c, 0xa7, 0x1b, 0x28, - 0xf9, 0x8b, 0x30, 0xa1, 0xfb, 0x33, 0xdc, 0x22, 0x09, 0x9d, 0x8b, 0x74, 0x17, 0x49, 0xe8, 0x7c, - 0xe4, 0xbb, 0x48, 0x77, 0x42, 0x7c, 0x10, 0xcd, 0xb4, 0xb5, 0x98, 0xf4, 0x25, 0xfb, 0xce, 0x3f, - 0x94, 0xc0, 0xf9, 0xdc, 0x6c, 0x0c, 0xde, 0x1f, 0xb6, 0x99, 0xec, 0x4b, 0x28, 0xc5, 0xfd, 0xe3, - 0x86, 0xe5, 0x6e, 0x7a, 0xc0, 0xdc, 0xb4, 0x07, 0xd5, 0xc2, 0x9d, 0xab, 0xe6, 0x60, 0x37, 0xf6, - 0x98, 0xf2, 0xb4, 0x93, 0x02, 0x7e, 0x04, 0x7f, 0x57, 0x02, 0xdf, 0xcc, 0xc3, 0xec, 0xe0, 0xee, - 0x08, 0x8d, 0x49, 0x26, 0x5b, 0x15, 0xdf, 0x3f, 0x46, 0x44, 0xee, 0xa9, 0x87, 0xcc, 0x53, 0x0f, - 0xe0, 0x0f, 0x8a, 0x78, 0x2a, 0x82, 0xd2, 0x7c, 0x06, 0x9a, 0x88, 0xaa, 0x2c, 0x7f, 0xfd, 0x57, - 0xe0, 0x4f, 0xec, 0x59, 0x04, 0x13, 0x16, 0x78, 0xd9, 0xe8, 0x43, 0x70, 0xc5, 0x9b, 0xa3, 0xc2, - 0x14, 0xbf, 0x30, 0x09, 0xc3, 0xd1, 0x9a, 0x31, 0x90, 0xd6, 0xa2, 0x7a, 0x32, 0xc5, 0xfe, 0xd5, - 0x49, 0x00, 0x12, 0xb5, 0x66, 0x7b, 0x94, 0x17, 0x9d, 0xd0, 0xea, 0x9d, 0xd1, 0x40, 0x46, 0x60, - 0x3c, 0x99, 0x35, 0xa5, 0xfa, 0xfd, 0x2f, 0x8e, 0xca, 0xc2, 0x97, 0x47, 0x65, 0xe1, 0x6f, 0x47, - 0x65, 0xe1, 0xd3, 0xe7, 0xe5, 0xb1, 0x2f, 0x9f, 0x97, 0xc7, 0xfe, 0xfc, 0xbc, 0x3c, 0xf6, 0x60, - 0xa3, 0x6e, 0x7a, 0x07, 0xcd, 0x47, 0xb2, 0x4e, 0x1a, 0xfc, 0x1f, 0xef, 0xc4, 0x66, 0xdf, 0x8d, - 0x36, 0x6b, 0x5d, 0x56, 0x3e, 0xec, 0xe8, 0xcd, 0xda, 0x0e, 0xa6, 0x8f, 0xa6, 0xd8, 0x5f, 0x1d, - 0x97, 0xfe, 0x1f, 0x00, 0x00, 0xff, 0xff, 0xb7, 0x4e, 0xa1, 0x75, 0x91, 0x20, 0x00, 0x00, + 0x15, 0x17, 0x57, 0x1f, 0x91, 0x46, 0xb1, 0x1d, 0x8f, 0xd5, 0x78, 0x4d, 0x29, 0xbb, 0x0a, 0xdd, + 0x8f, 0xb5, 0xec, 0x92, 0x92, 0x0c, 0x23, 0x8e, 0x5d, 0x47, 0xd6, 0x4a, 0xb2, 0xb3, 0xb0, 0x63, + 0x2b, 0xb4, 0xec, 0x16, 0x6e, 0x51, 0x9a, 0x22, 0xa7, 0x2b, 0x42, 0x5c, 0x0e, 0xc5, 0x19, 0xad, + 0xbd, 0x30, 0x72, 0x48, 0x0f, 0x6d, 0x8e, 0x41, 0x3f, 0xee, 0xb9, 0xf4, 0xd8, 0x1e, 0x7a, 0xe8, + 0xdf, 0x90, 0x5b, 0x53, 0xe4, 0x52, 0xf4, 0xe0, 0x16, 0x72, 0x0b, 0x14, 0x05, 0x5a, 0xb4, 0x46, + 0xaf, 0x05, 0x0a, 0x0e, 0x87, 0x5c, 0x72, 0x97, 0xbb, 0x4b, 0xee, 0x2a, 0xb7, 0xe5, 0xcc, 0x7b, + 0xbf, 0x79, 0xef, 0xcd, 0x9b, 0x37, 0xef, 0x37, 0x0b, 0x14, 0xcb, 0xa1, 0xc8, 0x33, 0xf6, 0x74, + 0xcb, 0xd1, 0x08, 0x32, 0x0e, 0x3d, 0x8b, 0xb6, 0x14, 0xc3, 0x68, 0x2a, 0xae, 0x87, 0x9b, 0x96, + 0x89, 0x3c, 0xa5, 0xb9, 0xa2, 0x1c, 0x1c, 0x22, 0xaf, 0x25, 0xbb, 0x1e, 0xa6, 0x18, 0x9e, 0x4f, + 0x51, 0x90, 0x0d, 0xa3, 0x29, 0x87, 0x0a, 0x72, 0x73, 0x45, 0x5c, 0xa8, 0x63, 0x5c, 0xb7, 0x91, + 0xa2, 0xbb, 0x96, 0xa2, 0x3b, 0x0e, 0xa6, 0x3a, 0xb5, 0xb0, 0x43, 0x02, 0x08, 0x71, 0xae, 0x8e, + 0xeb, 0x98, 0xfd, 0x54, 0xfc, 0x5f, 0x7c, 0xb4, 0xcc, 0x75, 0xd8, 0xd7, 0xee, 0xe1, 0x8f, 0x14, + 0x6a, 0x35, 0x10, 0xa1, 0x7a, 0xc3, 0xe5, 0x02, 0xab, 0x59, 0x4c, 0x8d, 0xac, 0x08, 0x74, 0x96, + 0x7b, 0xe9, 0x34, 0x57, 0x14, 0xb2, 0xa7, 0x7b, 0xc8, 0xd4, 0x0c, 0xec, 0x90, 0xc3, 0x46, 0xa4, + 0xf1, 0x8d, 0x3e, 0x1a, 0x4f, 0x2d, 0x0f, 0x71, 0xb1, 0x05, 0x8a, 0x1c, 0x13, 0x79, 0x0d, 0xcb, + 0xa1, 0x8a, 0xe1, 0xb5, 0x5c, 0x8a, 0x95, 0x7d, 0xd4, 0x0a, 0x3d, 0x3c, 0x67, 0x60, 0xd2, 0xc0, + 0x44, 0x0b, 0x9c, 0x0c, 0x3e, 0x82, 0x29, 0xe9, 0x2a, 0x98, 0xff, 0xd0, 0x0f, 0xe7, 0x06, 0x5f, + 0xf6, 0x36, 0x72, 0x10, 0xb1, 0x88, 0x8a, 0x0e, 0x0e, 0x11, 0xa1, 0xf0, 0x1c, 0x98, 0x0e, 0xd6, + 0xb6, 0xcc, 0xa2, 0xb0, 0x28, 0x54, 0x66, 0xd4, 0xd7, 0xd8, 0x77, 0xcd, 0x94, 0x9e, 0x83, 0x85, + 0x74, 0x4d, 0xe2, 0x62, 0x87, 0x20, 0xf8, 0x7d, 0x70, 0xa2, 0x1e, 0x0c, 0x69, 0x84, 0xea, 0x14, + 0x31, 0xfd, 0xd9, 0xd5, 0x65, 0xb9, 0xd7, 0x8e, 0x35, 0x57, 0xe4, 0x0e, 0xac, 0x07, 0xbe, 0x5e, + 0x75, 0xe2, 0xf3, 0x17, 0xe5, 0x31, 0xf5, 0xf5, 0x7a, 0x6c, 0x4c, 0x5a, 0x00, 0x62, 0x62, 0xf1, + 0x0d, 0x1f, 0x2e, 0xb4, 0x5a, 0xd2, 0x3b, 0x9c, 0x0a, 0x67, 0xb9, 0x65, 0x55, 0x30, 0xc5, 0x96, + 0x27, 0x45, 0x61, 0x71, 0xbc, 0x32, 0xbb, 0xba, 0x24, 0x67, 0x48, 0x22, 0x99, 0x81, 0xa8, 0x5c, + 0x53, 0xba, 0x00, 0xbe, 0xd5, 0xbd, 0xc4, 0x03, 0xaa, 0x7b, 0x74, 0xdb, 0xc3, 0x2e, 0x26, 0xba, + 0x1d, 0x59, 0xf3, 0x89, 0x00, 0x2a, 0x83, 0x65, 0xb9, 0x6d, 0x3f, 0x00, 0x33, 0x6e, 0x38, 0xc8, + 0x23, 0xf6, 0x5e, 0x36, 0xf3, 0x38, 0xf8, 0xba, 0x69, 0x5a, 0x7e, 0x76, 0xb7, 0xa1, 0xdb, 0x80, + 0x52, 0x05, 0x7c, 0x33, 0xcd, 0x12, 0xec, 0x76, 0x19, 0xfd, 0x13, 0x21, 0xdd, 0xc1, 0x84, 0x68, + 0xb4, 0xd3, 0x5d, 0x36, 0xdf, 0xc8, 0x65, 0xb3, 0x8a, 0x1a, 0xb8, 0xa9, 0xdb, 0xa9, 0x26, 0xff, + 0xad, 0x00, 0x26, 0xd9, 0xda, 0x7d, 0x72, 0x11, 0xce, 0x83, 0x19, 0xc3, 0xb6, 0x90, 0x43, 0xfd, + 0xb9, 0x02, 0x9b, 0x9b, 0x0e, 0x06, 0x6a, 0x26, 0x3c, 0x03, 0x26, 0x29, 0x76, 0xb5, 0x7b, 0xc5, + 0xf1, 0x45, 0xa1, 0x72, 0x42, 0x9d, 0xa0, 0xd8, 0xbd, 0x07, 0x97, 0x00, 0x6c, 0x58, 0x8e, 0xe6, + 0xe2, 0xa7, 0xc8, 0xd3, 0x2c, 0x47, 0x0b, 0x24, 0x26, 0x16, 0x85, 0xca, 0xb8, 0x7a, 0xb2, 0x61, + 0x39, 0xdb, 0xfe, 0x44, 0xcd, 0xd9, 0xf1, 0x65, 0x97, 0xc1, 0x5c, 0x53, 0xb7, 0x2d, 0x53, 0xa7, + 0xd8, 0x23, 0x5c, 0xc5, 0xd0, 0xdd, 0xe2, 0x24, 0xc3, 0x83, 0xed, 0x39, 0xa6, 0xb4, 0xa1, 0xbb, + 0x70, 0x09, 0x9c, 0x8e, 0x46, 0x35, 0x82, 0x28, 0x13, 0x9f, 0x62, 0xe2, 0xa7, 0xa2, 0x89, 0x07, + 0x88, 0xfa, 0xb2, 0x0b, 0x60, 0x46, 0xb7, 0x6d, 0xfc, 0xd4, 0xb6, 0x08, 0x2d, 0xbe, 0xb6, 0x38, + 0x5e, 0x99, 0x51, 0xdb, 0x03, 0x50, 0x04, 0xd3, 0x26, 0x72, 0x5a, 0x6c, 0x72, 0x9a, 0x4d, 0x46, + 0xdf, 0xbe, 0xd7, 0xbe, 0x0f, 0x84, 0xea, 0xfb, 0xa8, 0x38, 0xb3, 0x28, 0x54, 0x26, 0xd4, 0xe9, + 0x06, 0xcb, 0xac, 0x7d, 0x04, 0x65, 0x70, 0x86, 0xa1, 0x68, 0x96, 0xa3, 0x1b, 0xd4, 0x6a, 0x22, + 0xad, 0xe9, 0x6f, 0x0f, 0x58, 0x14, 0x2a, 0xd3, 0xea, 0x69, 0x36, 0x55, 0xe3, 0x33, 0x8f, 0xfc, + 0x38, 0xff, 0x54, 0x00, 0x6f, 0xb3, 0x0d, 0x7f, 0x14, 0xda, 0x17, 0xcb, 0x28, 0x6f, 0x70, 0x3d, + 0x80, 0x37, 0xc0, 0x1b, 0xe1, 0xde, 0x6a, 0xba, 0x69, 0x7a, 0x88, 0x90, 0x60, 0x2b, 0xaa, 0xf0, + 0xd5, 0x8b, 0xf2, 0xc9, 0x96, 0xde, 0xb0, 0xaf, 0x49, 0x7c, 0x42, 0x52, 0x4f, 0x85, 0xb2, 0xeb, + 0xc1, 0xc8, 0xb5, 0xe9, 0x4f, 0x3e, 0x2b, 0x8f, 0xfd, 0xfd, 0xb3, 0xf2, 0x98, 0x74, 0x1f, 0x48, + 0xfd, 0x0c, 0xe1, 0x49, 0x77, 0x01, 0xbc, 0x11, 0x96, 0xca, 0x68, 0xb9, 0xc0, 0xa2, 0x53, 0x46, + 0x4c, 0xde, 0x5f, 0xac, 0xdb, 0xb5, 0xed, 0xd8, 0xe2, 0xd9, 0x5c, 0xeb, 0x5a, 0xab, 0x8f, 0x6b, + 0x1d, 0xeb, 0xf7, 0x73, 0x2d, 0x69, 0x48, 0xdb, 0xb5, 0xae, 0x48, 0x72, 0xd7, 0x3a, 0xa2, 0x26, + 0xcd, 0x83, 0x73, 0x0c, 0x70, 0x67, 0xcf, 0xc3, 0x94, 0xda, 0x88, 0x55, 0xc7, 0xf0, 0x0c, 0xff, + 0x41, 0xe0, 0x55, 0xb2, 0x63, 0x96, 0x2f, 0x53, 0x06, 0xb3, 0xc4, 0xd6, 0xc9, 0x9e, 0xd6, 0x40, + 0x14, 0x79, 0x6c, 0x85, 0x71, 0x15, 0xb0, 0xa1, 0x0f, 0xfc, 0x11, 0xb8, 0x0a, 0xbe, 0x16, 0x13, + 0xd0, 0x58, 0xce, 0xe8, 0x8e, 0x81, 0x98, 0xef, 0xe3, 0xea, 0x99, 0xb6, 0xe8, 0x7a, 0x38, 0x05, + 0x7f, 0x08, 0x8a, 0x0e, 0x7a, 0x46, 0x35, 0x0f, 0xb9, 0x36, 0x72, 0x2c, 0xb2, 0xa7, 0x19, 0xba, + 0x63, 0xfa, 0xce, 0x22, 0x76, 0xfe, 0x66, 0x57, 0x45, 0x39, 0xb8, 0x59, 0xe5, 0xf0, 0x66, 0x95, + 0x77, 0xc2, 0x9b, 0xb5, 0x3a, 0xed, 0x97, 0xfa, 0x4f, 0xff, 0x5c, 0x16, 0xd4, 0x37, 0x7d, 0x14, + 0x35, 0x04, 0xd9, 0x08, 0x31, 0xa4, 0x4b, 0x60, 0x89, 0xb9, 0xa4, 0xa2, 0xba, 0x45, 0x28, 0xf2, + 0x90, 0xd9, 0x2e, 0x22, 0x4f, 0x75, 0xcf, 0xdc, 0x44, 0x0e, 0x6e, 0x44, 0x55, 0x6c, 0x0b, 0x5c, + 0xcc, 0x24, 0xcd, 0x23, 0xf2, 0x26, 0x98, 0x32, 0xd9, 0x08, 0xbb, 0x18, 0x66, 0x54, 0xfe, 0x25, + 0x95, 0xf8, 0x55, 0x17, 0x14, 0x28, 0x64, 0xb2, 0x7a, 0x54, 0xdb, 0x8c, 0x96, 0xf9, 0x58, 0x00, + 0x6f, 0xf5, 0x10, 0xe0, 0xc8, 0x4f, 0xc0, 0x49, 0x37, 0x3e, 0x17, 0x5e, 0x3d, 0xab, 0x99, 0xea, + 0x64, 0x02, 0x96, 0xdf, 0x87, 0x1d, 0x78, 0x52, 0x0d, 0x9c, 0x48, 0x88, 0xc1, 0x22, 0xe0, 0xf9, + 0xbb, 0x99, 0x4c, 0xe7, 0x4d, 0x58, 0x02, 0x20, 0xac, 0xaf, 0xb5, 0x4d, 0xb6, 0x99, 0x13, 0x6a, + 0x6c, 0x44, 0xba, 0x0b, 0x14, 0xe6, 0xcd, 0xba, 0x6d, 0x6f, 0xeb, 0x96, 0x47, 0x1e, 0xe9, 0xf6, + 0x06, 0x76, 0xfc, 0x94, 0xab, 0x26, 0xaf, 0x83, 0xda, 0x66, 0x86, 0x3e, 0xe1, 0x57, 0x02, 0x58, + 0xce, 0x0e, 0xc7, 0xe3, 0x75, 0x00, 0x4e, 0xbb, 0xba, 0xe5, 0xf9, 0x35, 0xcb, 0xef, 0x88, 0xd8, + 0x31, 0xe0, 0x21, 0xbb, 0x95, 0x2d, 0x64, 0xba, 0xe5, 0xb5, 0x17, 0x8a, 0x8e, 0x99, 0xd3, 0x4e, + 0x80, 0x93, 0x6e, 0x42, 0x44, 0xfa, 0xaf, 0x00, 0xde, 0x1e, 0xa8, 0x05, 0x6f, 0xf5, 0x3a, 0x9b, + 0xd5, 0xf9, 0x57, 0x2f, 0xca, 0x67, 0x83, 0x52, 0xd0, 0x29, 0xd1, 0x5d, 0xee, 0x7c, 0x9c, 0x1e, + 0x25, 0x25, 0x86, 0xd3, 0x29, 0xd1, 0x5d, 0x5b, 0xe0, 0x1a, 0x78, 0x3d, 0x92, 0xda, 0x47, 0x2d, + 0x7e, 0xc6, 0x16, 0xe4, 0x76, 0x3f, 0x28, 0x07, 0xfd, 0xa0, 0xbc, 0x7d, 0xb8, 0x6b, 0x5b, 0xc6, + 0x1d, 0xd4, 0x52, 0x67, 0x43, 0x8d, 0x3b, 0xa8, 0x25, 0xcd, 0x01, 0x18, 0xa4, 0xae, 0xee, 0xe9, + 0xed, 0x83, 0xf3, 0x04, 0x9c, 0x49, 0x8c, 0xf2, 0x6d, 0xa9, 0x81, 0x29, 0x97, 0x8d, 0xf0, 0x6b, + 0xfe, 0x62, 0xc6, 0xbd, 0xf0, 0x55, 0x78, 0xde, 0x72, 0x00, 0xe9, 0x36, 0x3f, 0xc8, 0x89, 0x0c, + 0xb8, 0xef, 0x52, 0x64, 0xd6, 0x9c, 0xa8, 0x3c, 0x66, 0xe9, 0x43, 0x0f, 0xf8, 0x19, 0x1f, 0x04, + 0x14, 0x35, 0x7f, 0x6f, 0xc5, 0x2f, 0xf3, 0x8e, 0x9d, 0x42, 0xe1, 0xd1, 0x9f, 0x8f, 0xdd, 0xea, + 0xc9, 0xad, 0x43, 0x44, 0xba, 0x0e, 0x4a, 0x89, 0x25, 0x73, 0xd9, 0xfb, 0x1f, 0x01, 0x2c, 0xf6, + 0xd0, 0x8e, 0x7e, 0xa5, 0x5e, 0xa6, 0x42, 0xe6, 0xcb, 0xb4, 0x2b, 0x2b, 0x0a, 0x39, 0xb3, 0x02, + 0xce, 0x81, 0x49, 0xd6, 0xe7, 0xb0, 0x7c, 0x1a, 0x57, 0x83, 0x0f, 0xf8, 0x0e, 0x98, 0xf0, 0xfc, + 0x42, 0x3e, 0xc1, 0x2c, 0x39, 0xef, 0xef, 0xe7, 0x9f, 0x5e, 0x94, 0xe7, 0x03, 0x42, 0x41, 0xcc, + 0x7d, 0xd9, 0xc2, 0x4a, 0x43, 0xa7, 0x7b, 0xf2, 0x5d, 0x54, 0xd7, 0x8d, 0xd6, 0x26, 0x32, 0x54, + 0xa6, 0xe0, 0xb7, 0xc0, 0xe5, 0x9e, 0x11, 0xe3, 0x1b, 0x83, 0x00, 0x68, 0xc7, 0x9c, 0x9f, 0xf5, + 0xad, 0x4c, 0xf9, 0x35, 0x28, 0x9a, 0x6a, 0x0c, 0x58, 0x3a, 0xe0, 0xd5, 0x28, 0xc9, 0x0d, 0x22, + 0xd9, 0xf7, 0x75, 0xb2, 0x83, 0xf9, 0x57, 0x78, 0x91, 0x8e, 0xb8, 0x1b, 0x92, 0x0e, 0x56, 0x72, + 0x2c, 0xc9, 0xc3, 0x71, 0x09, 0xc0, 0x68, 0x0b, 0xc3, 0x54, 0x0a, 0x93, 0x33, 0x2a, 0x1d, 0x41, + 0xd9, 0x34, 0x59, 0x8b, 0x73, 0x31, 0xbd, 0x69, 0xda, 0xc0, 0x8d, 0x86, 0x45, 0x88, 0x85, 0x1d, + 0x35, 0xe6, 0xd1, 0x57, 0xd6, 0xc7, 0x49, 0x75, 0x70, 0x29, 0x9b, 0x21, 0xdc, 0xcf, 0x30, 0xa7, + 0x84, 0xbc, 0x39, 0x25, 0xf1, 0x63, 0x54, 0xb5, 0xb1, 0xb1, 0x4f, 0x1e, 0x3a, 0xd4, 0xb2, 0xef, + 0xa1, 0x67, 0x74, 0xcb, 0xc5, 0xc6, 0x5e, 0x58, 0xc6, 0x1e, 0xf3, 0xc6, 0x2f, 0x5d, 0x86, 0x5b, + 0x70, 0x05, 0x9c, 0xdd, 0x65, 0xf3, 0xda, 0xa1, 0x2f, 0xa0, 0xb1, 0xfe, 0x05, 0xf9, 0x22, 0xcc, + 0xa8, 0x09, 0x75, 0x6e, 0x37, 0x45, 0x7d, 0xf5, 0x37, 0x65, 0x30, 0xc9, 0xc0, 0xe1, 0x91, 0x00, + 0xe6, 0xd2, 0xa8, 0x30, 0xbc, 0x99, 0x3f, 0x7d, 0x93, 0xfc, 0x5b, 0x5c, 0x1f, 0x01, 0x21, 0x70, + 0x4f, 0xda, 0xfa, 0xf1, 0x97, 0x7f, 0xfd, 0x79, 0x61, 0x0d, 0xde, 0x18, 0xfc, 0xb6, 0x12, 0x25, + 0x1c, 0xe7, 0xda, 0xca, 0xf3, 0x30, 0x49, 0x3e, 0x82, 0x5f, 0x0a, 0xfc, 0x4a, 0x48, 0x66, 0x31, + 0x5c, 0xcb, 0x6f, 0x61, 0x82, 0xac, 0x8b, 0x37, 0x87, 0x07, 0xe0, 0x1e, 0xbe, 0xcb, 0x3c, 0xbc, + 0x0c, 0x57, 0x72, 0x78, 0x18, 0xd0, 0x78, 0xf8, 0x71, 0x01, 0x14, 0x7b, 0x70, 0x73, 0x02, 0xef, + 0x0e, 0x69, 0x59, 0xea, 0x33, 0x80, 0xf8, 0xc1, 0x31, 0xa1, 0x71, 0xa7, 0xdf, 0x67, 0x4e, 0x57, + 0xe1, 0xcd, 0xbc, 0x4e, 0xfb, 0x7c, 0xd1, 0xa3, 0x5a, 0xc4, 0xb0, 0xe1, 0xff, 0x04, 0x70, 0x36, + 0x9d, 0xea, 0x13, 0x78, 0x67, 0x68, 0xa3, 0xbb, 0xdf, 0x14, 0xc4, 0xbb, 0xc7, 0x03, 0xc6, 0x03, + 0x70, 0x9b, 0x05, 0x60, 0x1d, 0xae, 0x0d, 0x11, 0x00, 0xec, 0xc6, 0xfc, 0xff, 0x77, 0x48, 0x93, + 0x52, 0x09, 0x27, 0xbc, 0x95, 0xdd, 0xea, 0x7e, 0xd4, 0x59, 0xbc, 0x3d, 0x32, 0x0e, 0x77, 0x7c, + 0x9d, 0x39, 0x7e, 0x1d, 0xbe, 0x9b, 0xe1, 0xb1, 0x34, 0x7a, 0x84, 0x48, 0xb4, 0x92, 0x29, 0x2e, + 0xc7, 0x9b, 0x9c, 0xa1, 0x5c, 0x4e, 0xa1, 0xd4, 0x43, 0xb9, 0x9c, 0xc6, 0x88, 0x87, 0x73, 0x39, + 0x71, 0x8b, 0xc1, 0xdf, 0x0b, 0xbc, 0xd1, 0x4d, 0x90, 0x61, 0xf8, 0x5e, 0x76, 0x13, 0xd3, 0x38, + 0xb6, 0xb8, 0x36, 0xb4, 0x3e, 0x77, 0xed, 0x2a, 0x73, 0x6d, 0x15, 0x2e, 0x0f, 0x76, 0x8d, 0x72, + 0x80, 0xe0, 0x3d, 0x15, 0xfe, 0xb2, 0x00, 0xce, 0x67, 0x60, 0xb7, 0xf0, 0x7e, 0x76, 0x13, 0x33, + 0xb1, 0x6a, 0x71, 0xfb, 0xf8, 0x00, 0x79, 0x10, 0xee, 0xb0, 0x20, 0x6c, 0xc1, 0x8d, 0xc1, 0x41, + 0xf0, 0x22, 0xc4, 0x76, 0x4e, 0x7b, 0x0c, 0x53, 0x0b, 0xd8, 0x3a, 0xfc, 0x47, 0x17, 0x1b, 0x4f, + 0x92, 0x4c, 0x02, 0x73, 0xdc, 0xaa, 0x3d, 0x28, 0xbf, 0x58, 0x1d, 0x05, 0x82, 0x7b, 0x5d, 0x65, + 0x5e, 0x7f, 0x07, 0x5e, 0x1b, 0xec, 0x75, 0x48, 0xf6, 0xb5, 0xce, 0x0b, 0xec, 0x17, 0x05, 0xfe, + 0xb8, 0x9c, 0x81, 0x5d, 0xc3, 0x9d, 0xec, 0x46, 0x67, 0xe7, 0xfe, 0xe2, 0xc3, 0x63, 0x46, 0xe5, + 0xd1, 0xb9, 0xce, 0xa2, 0x73, 0x05, 0x5e, 0xce, 0x5d, 0xdf, 0x2d, 0x13, 0xfe, 0x56, 0x00, 0xb3, + 0x31, 0x02, 0x0b, 0xdf, 0xc9, 0xb1, 0x5d, 0x71, 0x22, 0x2c, 0x5e, 0xcd, 0xaf, 0xc8, 0xed, 0x5f, + 0x66, 0xf6, 0x2f, 0xc1, 0x4a, 0x86, 0xdd, 0x0d, 0x8c, 0xfc, 0x59, 0x78, 0xa0, 0xfb, 0x53, 0xd9, + 0x3c, 0x07, 0x3a, 0x13, 0xbb, 0xce, 0x73, 0xa0, 0xb3, 0xb1, 0xec, 0x3c, 0xdd, 0x09, 0xf6, 0x41, + 0x34, 0xcb, 0xd1, 0xda, 0x24, 0x2d, 0xde, 0x77, 0xfe, 0xae, 0x00, 0x2e, 0x64, 0x66, 0x4f, 0xf0, + 0xe1, 0xb0, 0xcd, 0x64, 0x5f, 0x02, 0x28, 0x3e, 0x3a, 0x6e, 0x58, 0x1e, 0xa6, 0xc7, 0x2c, 0x4c, + 0x3b, 0x50, 0xcd, 0xdd, 0xb9, 0x6a, 0x2e, 0xf2, 0xda, 0x11, 0x53, 0x9e, 0x77, 0x52, 0xb6, 0x8f, + 0xe0, 0xaf, 0x0b, 0xe0, 0xeb, 0x59, 0x98, 0x18, 0xdc, 0x1e, 0xa1, 0x31, 0x49, 0x65, 0x97, 0xe2, + 0x87, 0xc7, 0x88, 0xc8, 0x23, 0xf5, 0x84, 0x45, 0xea, 0x31, 0xfc, 0x5e, 0x9e, 0x48, 0x45, 0x50, + 0x9a, 0xcf, 0x18, 0x63, 0x59, 0x95, 0x16, 0xaf, 0x7f, 0x75, 0xb6, 0xc1, 0xb1, 0x13, 0xb7, 0x31, + 0xca, 0x3b, 0x44, 0x18, 0x95, 0xcd, 0xd1, 0x40, 0x46, 0xe8, 0xfb, 0xd3, 0x4f, 0xd6, 0x3f, 0x05, + 0xfe, 0xe7, 0x41, 0x1a, 0x3b, 0x86, 0x39, 0x9e, 0x5e, 0xfa, 0x30, 0x70, 0xf1, 0xd6, 0xa8, 0x30, + 0xf9, 0x3b, 0xc0, 0x1e, 0x64, 0xbe, 0xfa, 0xdd, 0xcf, 0x8f, 0x4a, 0xc2, 0x17, 0x47, 0x25, 0xe1, + 0x2f, 0x47, 0x25, 0xe1, 0xd3, 0x97, 0xa5, 0xb1, 0x2f, 0x5e, 0x96, 0xc6, 0xfe, 0xf8, 0xb2, 0x34, + 0xf6, 0xf8, 0x46, 0xdd, 0xa2, 0x7b, 0x87, 0xbb, 0xb2, 0x81, 0x1b, 0xfc, 0xdf, 0xf1, 0xd8, 0x2a, + 0xdf, 0x8e, 0x56, 0x69, 0x5e, 0x51, 0x9e, 0x75, 0x74, 0x64, 0x2d, 0x17, 0x91, 0xdd, 0x29, 0xf6, + 0x4f, 0xc6, 0xe5, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0x7d, 0x81, 0x96, 0x00, 0xbd, 0x20, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1968,12 +1984,13 @@ type QueryClient interface { // 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) // 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) + // QueryBlocksUntilNextEpoch returns the number of blocks until the next epoch + // starts and validator updates are sent to the consumer chains + QueryBlocksUntilNextEpoch(ctx context.Context, in *QueryBlocksUntilNextEpochRequest, opts ...grpc.CallOption) (*QueryBlocksUntilNextEpochResponse, error) } type queryClient struct { @@ -2110,18 +2127,18 @@ func (c *queryClient) QueryValidatorConsumerCommissionRate(ctx context.Context, 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...) +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 } -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...) +func (c *queryClient) QueryBlocksUntilNextEpoch(ctx context.Context, in *QueryBlocksUntilNextEpochRequest, opts ...grpc.CallOption) (*QueryBlocksUntilNextEpochResponse, error) { + out := new(QueryBlocksUntilNextEpochResponse) + err := c.cc.Invoke(ctx, "/interchain_security.ccv.provider.v1.Query/QueryBlocksUntilNextEpoch", in, out, opts...) if err != nil { return nil, err } @@ -2169,12 +2186,13 @@ type QueryServer interface { // 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) // 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) + // QueryBlocksUntilNextEpoch returns the number of blocks until the next epoch + // starts and validator updates are sent to the consumer chains + QueryBlocksUntilNextEpoch(context.Context, *QueryBlocksUntilNextEpochRequest) (*QueryBlocksUntilNextEpochResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -2223,12 +2241,12 @@ func (*UnimplementedQueryServer) QueryConsumerChainsValidatorHasToValidate(ctx c 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") -} func (*UnimplementedQueryServer) QueryConsumerValidators(ctx context.Context, req *QueryConsumerValidatorsRequest) (*QueryConsumerValidatorsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryConsumerValidators not implemented") } +func (*UnimplementedQueryServer) QueryBlocksUntilNextEpoch(ctx context.Context, req *QueryBlocksUntilNextEpochRequest) (*QueryBlocksUntilNextEpochResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryBlocksUntilNextEpoch not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -2486,38 +2504,38 @@ func _Query_QueryValidatorConsumerCommissionRate_Handler(srv interface{}, ctx co 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) +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).QueryOldestUnconfirmedVsc(ctx, in) + return srv.(QueryServer).QueryConsumerValidators(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/interchain_security.ccv.provider.v1.Query/QueryOldestUnconfirmedVsc", + FullMethod: "/interchain_security.ccv.provider.v1.Query/QueryConsumerValidators", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryOldestUnconfirmedVsc(ctx, req.(*QueryOldestUnconfirmedVscRequest)) + return srv.(QueryServer).QueryConsumerValidators(ctx, req.(*QueryConsumerValidatorsRequest)) } 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) +func _Query_QueryBlocksUntilNextEpoch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryBlocksUntilNextEpochRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(QueryServer).QueryConsumerValidators(ctx, in) + return srv.(QueryServer).QueryBlocksUntilNextEpoch(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/interchain_security.ccv.provider.v1.Query/QueryConsumerValidators", + FullMethod: "/interchain_security.ccv.provider.v1.Query/QueryBlocksUntilNextEpoch", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).QueryConsumerValidators(ctx, req.(*QueryConsumerValidatorsRequest)) + return srv.(QueryServer).QueryBlocksUntilNextEpoch(ctx, req.(*QueryBlocksUntilNextEpochRequest)) } return interceptor(ctx, in, info, handler) } @@ -2582,14 +2600,14 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "QueryValidatorConsumerCommissionRate", Handler: _Query_QueryValidatorConsumerCommissionRate_Handler, }, - { - MethodName: "QueryOldestUnconfirmedVsc", - Handler: _Query_QueryOldestUnconfirmedVsc_Handler, - }, { MethodName: "QueryConsumerValidators", Handler: _Query_QueryConsumerValidators_Handler, }, + { + MethodName: "QueryBlocksUntilNextEpoch", + Handler: _Query_QueryBlocksUntilNextEpoch_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "interchain_security/ccv/provider/v1/query.proto", @@ -2854,6 +2872,21 @@ func (m *Chain) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.AllowInactiveVals { + i-- + if m.AllowInactiveVals { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x50 + } + if m.MinStake != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.MinStake)) + i-- + dAtA[i] = 0x48 + } if len(m.Denylist) > 0 { for iNdEx := len(m.Denylist) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Denylist[iNdEx]) @@ -3541,6 +3574,16 @@ func (m *QueryConsumerValidatorsValidator) MarshalToSizedBuffer(dAtA []byte) (in _ = 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] = 0x22 if m.Power != 0 { i = encodeVarintQuery(dAtA, i, uint64(m.Power)) i-- @@ -3737,7 +3780,7 @@ func (m *QueryValidatorConsumerCommissionRateResponse) MarshalToSizedBuffer(dAtA return len(dAtA) - i, nil } -func (m *QueryOldestUnconfirmedVscRequest) Marshal() (dAtA []byte, err error) { +func (m *QueryBlocksUntilNextEpochRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -3747,27 +3790,20 @@ func (m *QueryOldestUnconfirmedVscRequest) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *QueryOldestUnconfirmedVscRequest) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryBlocksUntilNextEpochRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryOldestUnconfirmedVscRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryBlocksUntilNextEpochRequest) 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 *QueryOldestUnconfirmedVscResponse) Marshal() (dAtA []byte, err error) { +func (m *QueryBlocksUntilNextEpochResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -3777,26 +3813,21 @@ func (m *QueryOldestUnconfirmedVscResponse) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *QueryOldestUnconfirmedVscResponse) MarshalTo(dAtA []byte) (int, error) { +func (m *QueryBlocksUntilNextEpochResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *QueryOldestUnconfirmedVscResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *QueryBlocksUntilNextEpochResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - { - size, err := m.VscSendTimestamp.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintQuery(dAtA, i, uint64(size)) + if m.BlocksUntilNextEpoch != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.BlocksUntilNextEpoch)) + i-- + dAtA[i] = 0x8 } - i-- - dAtA[i] = 0xa return len(dAtA) - i, nil } @@ -3941,6 +3972,12 @@ func (m *Chain) Size() (n int) { n += 1 + l + sovQuery(uint64(l)) } } + if m.MinStake != 0 { + n += 1 + sovQuery(uint64(m.MinStake)) + } + if m.AllowInactiveVals { + n += 2 + } return n } @@ -4221,6 +4258,8 @@ func (m *QueryConsumerValidatorsValidator) Size() (n int) { if m.Power != 0 { n += 1 + sovQuery(uint64(m.Power)) } + l = m.Rate.Size() + n += 1 + l + sovQuery(uint64(l)) return n } @@ -4295,27 +4334,24 @@ func (m *QueryValidatorConsumerCommissionRateResponse) Size() (n int) { return n } -func (m *QueryOldestUnconfirmedVscRequest) Size() (n int) { +func (m *QueryBlocksUntilNextEpochRequest) 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 *QueryOldestUnconfirmedVscResponse) Size() (n int) { +func (m *QueryBlocksUntilNextEpochResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l - l = m.VscSendTimestamp.Size() - n += 1 + l + sovQuery(uint64(l)) + if m.BlocksUntilNextEpoch != 0 { + n += 1 + sovQuery(uint64(m.BlocksUntilNextEpoch)) + } return n } @@ -5129,6 +5165,45 @@ func (m *Chain) Unmarshal(dAtA []byte) error { } m.Denylist = append(m.Denylist, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MinStake", wireType) + } + m.MinStake = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MinStake |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowInactiveVals", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowInactiveVals = bool(v != 0) default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) @@ -6891,6 +6966,40 @@ func (m *QueryConsumerValidatorsValidator) Unmarshal(dAtA []byte) error { break } } + case 4: + 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:]) @@ -7358,7 +7467,7 @@ func (m *QueryValidatorConsumerCommissionRateResponse) Unmarshal(dAtA []byte) er } return nil } -func (m *QueryOldestUnconfirmedVscRequest) Unmarshal(dAtA []byte) error { +func (m *QueryBlocksUntilNextEpochRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -7381,44 +7490,12 @@ func (m *QueryOldestUnconfirmedVscRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryOldestUnconfirmedVscRequest: wiretype end group for non-group") + return fmt.Errorf("proto: QueryBlocksUntilNextEpochRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryOldestUnconfirmedVscRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryBlocksUntilNextEpochRequest: 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:]) @@ -7440,7 +7517,7 @@ func (m *QueryOldestUnconfirmedVscRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryOldestUnconfirmedVscResponse) Unmarshal(dAtA []byte) error { +func (m *QueryBlocksUntilNextEpochResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -7463,17 +7540,17 @@ func (m *QueryOldestUnconfirmedVscResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: QueryOldestUnconfirmedVscResponse: wiretype end group for non-group") + return fmt.Errorf("proto: QueryBlocksUntilNextEpochResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: QueryOldestUnconfirmedVscResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: QueryBlocksUntilNextEpochResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field VscSendTimestamp", wireType) + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlocksUntilNextEpoch", wireType) } - var msglen int + m.BlocksUntilNextEpoch = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowQuery @@ -7483,25 +7560,11 @@ func (m *QueryOldestUnconfirmedVscResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + m.BlocksUntilNextEpoch |= uint64(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 err := m.VscSendTimestamp.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) diff --git a/x/ccv/provider/types/query.pb.gw.go b/x/ccv/provider/types/query.pb.gw.go index 16bb010d71..a115ace156 100644 --- a/x/ccv/provider/types/query.pb.gw.go +++ b/x/ccv/provider/types/query.pb.gw.go @@ -505,8 +505,8 @@ func local_request_Query_QueryValidatorConsumerCommissionRate_0(ctx context.Cont } -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 +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 ( @@ -527,13 +527,13 @@ func request_Query_QueryOldestUnconfirmedVsc_0(ctx context.Context, marshaler ru return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chain_id", err) } - msg, err := client.QueryOldestUnconfirmedVsc(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.QueryConsumerValidators(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_Query_QueryOldestUnconfirmedVsc_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryOldestUnconfirmedVscRequest +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 ( @@ -554,61 +554,25 @@ func local_request_Query_QueryOldestUnconfirmedVsc_0(ctx context.Context, marsha return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chain_id", err) } - msg, err := server.QueryOldestUnconfirmedVsc(ctx, &protoReq) + msg, err := server.QueryConsumerValidators(ctx, &protoReq) return msg, metadata, err } -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 +func request_Query_QueryBlocksUntilNextEpoch_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBlocksUntilNextEpochRequest 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)) + msg, err := client.QueryBlocksUntilNextEpoch(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 +func local_request_Query_QueryBlocksUntilNextEpoch_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBlocksUntilNextEpochRequest 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) + msg, err := server.QueryBlocksUntilNextEpoch(ctx, &protoReq) return msg, metadata, err } @@ -941,7 +905,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) - mux.Handle("GET", pattern_Query_QueryOldestUnconfirmedVsc_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + 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 @@ -952,7 +916,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryOldestUnconfirmedVsc_0(rctx, inboundMarshaler, server, req, pathParams) + 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 { @@ -960,11 +924,11 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } - forward_Query_QueryOldestUnconfirmedVsc_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_QueryConsumerValidators_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryConsumerValidators_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_QueryBlocksUntilNextEpoch_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream @@ -975,7 +939,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_Query_QueryConsumerValidators_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_Query_QueryBlocksUntilNextEpoch_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 { @@ -983,7 +947,7 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv return } - forward_Query_QueryConsumerValidators_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_QueryBlocksUntilNextEpoch_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -1308,7 +1272,7 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) - mux.Handle("GET", pattern_Query_QueryOldestUnconfirmedVsc_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + 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) @@ -1317,18 +1281,18 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryOldestUnconfirmedVsc_0(rctx, inboundMarshaler, client, req, pathParams) + 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_QueryOldestUnconfirmedVsc_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_Query_QueryConsumerValidators_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_Query_QueryConsumerValidators_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_Query_QueryBlocksUntilNextEpoch_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) @@ -1337,14 +1301,14 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_Query_QueryConsumerValidators_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_Query_QueryBlocksUntilNextEpoch_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()...) + forward_Query_QueryBlocksUntilNextEpoch_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -1380,9 +1344,9 @@ 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))) + + pattern_Query_QueryBlocksUntilNextEpoch_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"interchain_security", "ccv", "provider", "blocks_until_next_epoch"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( @@ -1414,7 +1378,7 @@ var ( forward_Query_QueryValidatorConsumerCommissionRate_0 = runtime.ForwardResponseMessage - forward_Query_QueryOldestUnconfirmedVsc_0 = runtime.ForwardResponseMessage - forward_Query_QueryConsumerValidators_0 = runtime.ForwardResponseMessage + + forward_Query_QueryBlocksUntilNextEpoch_0 = runtime.ForwardResponseMessage ) diff --git a/x/ccv/provider/types/tx.pb.go b/x/ccv/provider/types/tx.pb.go index be71e8fa18..292e870a9b 100644 --- a/x/ccv/provider/types/tx.pb.go +++ b/x/ccv/provider/types/tx.pb.go @@ -424,7 +424,7 @@ type MsgConsumerAddition struct { // channel is created on top of the same connection as the CCV channel. // Note that transfer_channel_id is the ID of the channel end on the consumer // chain. it is most relevant for chains performing a sovereign to consumer - // changeover in order to maintan the existing ibc transfer channel + // changeover in order to maintain the existing ibc transfer channel DistributionTransmissionChannel string `protobuf:"bytes,12,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 @@ -447,6 +447,10 @@ type MsgConsumerAddition struct { Denylist []string `protobuf:"bytes,17,rep,name=denylist,proto3" json:"denylist,omitempty"` // signer address Authority string `protobuf:"bytes,18,opt,name=authority,proto3" json:"authority,omitempty"` + // Corresponds to the minimal amount of (provider chain) stake required to validate on the consumer chain. + MinStake uint64 `protobuf:"varint,19,opt,name=min_stake,json=minStake,proto3" json:"min_stake,omitempty"` + // Corresponds to whether inactive validators are allowed to validate the consumer chain. + AllowInactiveVals bool `protobuf:"varint,20,opt,name=allow_inactive_vals,json=allowInactiveVals,proto3" json:"allow_inactive_vals,omitempty"` } func (m *MsgConsumerAddition) Reset() { *m = MsgConsumerAddition{} } @@ -608,6 +612,20 @@ func (m *MsgConsumerAddition) GetAuthority() string { return "" } +func (m *MsgConsumerAddition) GetMinStake() uint64 { + if m != nil { + return m.MinStake + } + return 0 +} + +func (m *MsgConsumerAddition) GetAllowInactiveVals() bool { + if m != nil { + return m.AllowInactiveVals + } + return false +} + // MsgConsumerAdditionResponse defines response type for MsgConsumerAddition messages type MsgConsumerAdditionResponse struct { } @@ -1027,6 +1045,8 @@ type MsgSetConsumerCommissionRate struct { // The rate to charge delegators on the consumer chain, as a fraction // TODO: migrate rate from sdk.Dec to math.LegacyDec Rate cosmossdk_io_math.LegacyDec `protobuf:"bytes,3,opt,name=rate,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"rate"` + // signer address + Signer string `protobuf:"bytes,4,opt,name=signer,proto3" json:"signer,omitempty"` } func (m *MsgSetConsumerCommissionRate) Reset() { *m = MsgSetConsumerCommissionRate{} } @@ -1131,6 +1151,10 @@ type MsgConsumerModification struct { Denylist []string `protobuf:"bytes,8,rep,name=denylist,proto3" json:"denylist,omitempty"` // signer address Authority string `protobuf:"bytes,9,opt,name=authority,proto3" json:"authority,omitempty"` + // Corresponds to the minimal amount of (provider chain) stake required to validate on the consumer chain. + MinStake uint64 `protobuf:"varint,10,opt,name=min_stake,json=minStake,proto3" json:"min_stake,omitempty"` + // Corresponds to whether inactive validators are allowed to validate the consumer chain. + AllowInactiveVals bool `protobuf:"varint,11,opt,name=allow_inactive_vals,json=allowInactiveVals,proto3" json:"allow_inactive_vals,omitempty"` } func (m *MsgConsumerModification) Reset() { *m = MsgConsumerModification{} } @@ -1229,6 +1253,20 @@ func (m *MsgConsumerModification) GetAuthority() string { return "" } +func (m *MsgConsumerModification) GetMinStake() uint64 { + if m != nil { + return m.MinStake + } + return 0 +} + +func (m *MsgConsumerModification) GetAllowInactiveVals() bool { + if m != nil { + return m.AllowInactiveVals + } + return false +} + type MsgConsumerModificationResponse struct { } @@ -1295,113 +1333,118 @@ func init() { } var fileDescriptor_43221a4391e9fbf4 = []byte{ - // 1687 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xcf, 0x6f, 0xe4, 0x48, - 0x15, 0x8e, 0xf3, 0x6b, 0xba, 0xab, 0xf3, 0xd3, 0x93, 0x21, 0x1d, 0x6f, 0xb6, 0x3b, 0x69, 0x96, - 0xdd, 0x68, 0xd8, 0xd8, 0x9b, 0xc0, 0xee, 0x42, 0xb4, 0x08, 0x92, 0xf4, 0xc0, 0x64, 0x21, 0x93, - 0xe0, 0x09, 0x8b, 0x04, 0x12, 0x56, 0xb5, 0x5d, 0x71, 0x97, 0xc6, 0x76, 0x59, 0x55, 0xd5, 0x9d, - 0xed, 0x1b, 0xda, 0x13, 0x12, 0x02, 0x2d, 0x37, 0xc4, 0x69, 0x0f, 0x08, 0x09, 0x09, 0xa4, 0x39, - 0xec, 0x89, 0x1b, 0xe2, 0x32, 0x07, 0x0e, 0xcb, 0x8a, 0x03, 0xe2, 0x30, 0xa0, 0x99, 0xc3, 0x72, - 0xe6, 0x2f, 0x40, 0x55, 0x2e, 0xbb, 0xdd, 0x49, 0xa7, 0xa7, 0xbb, 0x03, 0x07, 0x2e, 0xad, 0x76, - 0xbd, 0xef, 0x7d, 0xf5, 0x7d, 0xcf, 0xae, 0x57, 0x65, 0x83, 0xd7, 0x71, 0xc4, 0x11, 0x75, 0x9b, - 0x10, 0x47, 0x0e, 0x43, 0x6e, 0x8b, 0x62, 0xde, 0xb1, 0x5c, 0xb7, 0x6d, 0xc5, 0x94, 0xb4, 0xb1, - 0x87, 0xa8, 0xd5, 0xde, 0xb1, 0xf8, 0xfb, 0x66, 0x4c, 0x09, 0x27, 0xfa, 0xe7, 0xfb, 0xa0, 0x4d, - 0xd7, 0x6d, 0x9b, 0x29, 0xda, 0x6c, 0xef, 0x18, 0xcb, 0x30, 0xc4, 0x11, 0xb1, 0xe4, 0x6f, 0x92, - 0x67, 0xac, 0xfb, 0x84, 0xf8, 0x01, 0xb2, 0x60, 0x8c, 0x2d, 0x18, 0x45, 0x84, 0x43, 0x8e, 0x49, - 0xc4, 0x54, 0xb4, 0xaa, 0xa2, 0xf2, 0xaa, 0xd1, 0x3a, 0xb7, 0x38, 0x0e, 0x11, 0xe3, 0x30, 0x8c, - 0x15, 0xa0, 0x72, 0x19, 0xe0, 0xb5, 0xa8, 0x64, 0x50, 0xf1, 0xb5, 0xcb, 0x71, 0x18, 0x75, 0x54, - 0x68, 0xc5, 0x27, 0x3e, 0x91, 0x7f, 0x2d, 0xf1, 0x2f, 0x4d, 0x70, 0x09, 0x0b, 0x09, 0x73, 0x92, - 0x40, 0x72, 0xa1, 0x42, 0xab, 0xc9, 0x95, 0x15, 0x32, 0x5f, 0x58, 0x0f, 0x99, 0x9f, 0xaa, 0xc4, - 0x0d, 0xd7, 0x72, 0x09, 0x45, 0x96, 0x1b, 0x60, 0x14, 0x71, 0x11, 0x4d, 0xfe, 0x29, 0xc0, 0xee, - 0x30, 0xa5, 0xcc, 0x0a, 0x95, 0xe4, 0x58, 0x82, 0x34, 0xc0, 0x7e, 0x93, 0x27, 0x54, 0xcc, 0xe2, - 0x28, 0xf2, 0x10, 0x0d, 0x71, 0x32, 0x41, 0xf7, 0x2a, 0x55, 0x91, 0x8b, 0xf3, 0x4e, 0x8c, 0x98, - 0x85, 0x04, 0x5f, 0xe4, 0xa2, 0x04, 0x50, 0xfb, 0xab, 0x06, 0x56, 0x8e, 0x99, 0xbf, 0xcf, 0x18, - 0xf6, 0xa3, 0x43, 0x12, 0xb1, 0x56, 0x88, 0xe8, 0xb7, 0x51, 0x47, 0x5f, 0x03, 0x85, 0x44, 0x1b, - 0xf6, 0xca, 0xda, 0x86, 0xb6, 0x55, 0xb4, 0x6f, 0xc9, 0xeb, 0x23, 0x4f, 0x7f, 0x1b, 0xcc, 0xa7, - 0xba, 0x1c, 0xe8, 0x79, 0xb4, 0x3c, 0x29, 0xe2, 0x07, 0xfa, 0xbf, 0x9f, 0x56, 0x17, 0x3a, 0x30, - 0x0c, 0xf6, 0x6a, 0x62, 0x14, 0x31, 0x56, 0xb3, 0xe7, 0x52, 0xe0, 0xbe, 0xe7, 0x51, 0x7d, 0x13, - 0xcc, 0xb9, 0x6a, 0x0a, 0xe7, 0x11, 0xea, 0x94, 0xa7, 0x24, 0x6f, 0xc9, 0xcd, 0x4d, 0xfb, 0x06, - 0x98, 0x15, 0x4a, 0x10, 0x2d, 0x4f, 0x4b, 0xd2, 0xf2, 0xa7, 0x1f, 0x6f, 0xaf, 0xa8, 0x8a, 0xef, - 0x27, 0xac, 0x0f, 0x39, 0xc5, 0x91, 0x6f, 0x2b, 0xdc, 0xde, 0xed, 0x9f, 0x7c, 0x54, 0x9d, 0xf8, - 0xd7, 0x47, 0xd5, 0x89, 0x0f, 0x3e, 0x7b, 0x7c, 0x57, 0x0d, 0xd6, 0x2a, 0x60, 0xbd, 0x9f, 0x2b, - 0x1b, 0xb1, 0x98, 0x44, 0x0c, 0xd5, 0xfe, 0xa8, 0x81, 0x97, 0x8f, 0x99, 0xff, 0xb0, 0xd5, 0x08, - 0x31, 0x4f, 0x01, 0xc7, 0x98, 0x35, 0x50, 0x13, 0xb6, 0x31, 0x69, 0x51, 0xfd, 0x2d, 0x50, 0x64, - 0x32, 0xca, 0x11, 0x4d, 0x0a, 0x30, 0x40, 0x4b, 0x17, 0xaa, 0x9f, 0x82, 0xb9, 0x30, 0xc7, 0x23, - 0x6b, 0x53, 0xda, 0x7d, 0xdd, 0xc4, 0x0d, 0xd7, 0xcc, 0xdf, 0x39, 0x33, 0x77, 0xaf, 0xda, 0x3b, - 0x66, 0x7e, 0x6e, 0xbb, 0x87, 0x61, 0xef, 0x73, 0x79, 0x83, 0xdd, 0x99, 0x6a, 0xaf, 0x81, 0x2f, - 0x0c, 0xb4, 0x90, 0x99, 0x7d, 0x3c, 0xd9, 0xc7, 0x6c, 0x9d, 0xb4, 0x1a, 0x01, 0x7a, 0x8f, 0x70, - 0x1c, 0xf9, 0x63, 0x9b, 0x75, 0xc0, 0xaa, 0xd7, 0x8a, 0x03, 0xec, 0x42, 0x8e, 0x9c, 0x36, 0xe1, - 0xc8, 0x49, 0x1f, 0x2f, 0xe5, 0xfb, 0xb5, 0xbc, 0x4d, 0xf9, 0x00, 0x9a, 0xf5, 0x34, 0xe1, 0x3d, - 0xc2, 0xd1, 0x3d, 0x05, 0xb7, 0xef, 0x78, 0xfd, 0x86, 0xf5, 0x1f, 0x81, 0x55, 0x1c, 0x9d, 0x53, - 0xe8, 0x8a, 0xe5, 0xeb, 0x34, 0x02, 0xe2, 0x3e, 0x72, 0x9a, 0x08, 0x7a, 0x88, 0xca, 0x87, 0xa7, - 0xb4, 0xfb, 0xea, 0x8b, 0x0a, 0x7b, 0x5f, 0xa2, 0xed, 0x3b, 0x5d, 0x9a, 0x03, 0xc1, 0x92, 0x0c, - 0x8f, 0x54, 0xdb, 0x7c, 0xc5, 0xb2, 0xda, 0xfe, 0x5a, 0x03, 0x8b, 0xc7, 0xcc, 0xff, 0x5e, 0xec, - 0x41, 0x8e, 0x4e, 0x21, 0x85, 0x21, 0x13, 0xd5, 0x84, 0x2d, 0xde, 0x24, 0x62, 0x45, 0xbf, 0xb8, - 0x9a, 0x19, 0x54, 0x3f, 0x02, 0xb3, 0xb1, 0x64, 0x50, 0xc5, 0xfb, 0xa2, 0x39, 0x44, 0xff, 0x34, - 0x93, 0x49, 0x0f, 0xa6, 0x9f, 0x3c, 0xad, 0x4e, 0xd8, 0x8a, 0x60, 0x6f, 0x41, 0xfa, 0xc9, 0xa8, - 0x6b, 0x6b, 0x60, 0xf5, 0x92, 0xca, 0xcc, 0xc1, 0xcf, 0x0a, 0xe0, 0xf6, 0x31, 0xf3, 0x53, 0x97, - 0xfb, 0x9e, 0x87, 0x45, 0x95, 0x06, 0x35, 0x80, 0x6f, 0x81, 0x05, 0x1c, 0x61, 0x8e, 0x61, 0xe0, - 0x34, 0x91, 0x28, 0xbd, 0x12, 0x6c, 0xc8, 0x9b, 0x21, 0x9a, 0x9e, 0xa9, 0x5a, 0x9d, 0xbc, 0x01, - 0x02, 0xa1, 0xf4, 0xcd, 0xab, 0xbc, 0x64, 0x50, 0x34, 0x04, 0x1f, 0x45, 0x88, 0x61, 0xe6, 0x34, - 0x21, 0x6b, 0xca, 0x7b, 0x3a, 0x67, 0x97, 0xd4, 0xd8, 0x7d, 0xc8, 0x9a, 0x7a, 0x15, 0x94, 0x1a, - 0x38, 0x82, 0xb4, 0x93, 0x20, 0xa6, 0x25, 0x02, 0x24, 0x43, 0x12, 0x70, 0x08, 0x00, 0x8b, 0xe1, - 0x45, 0xe4, 0x88, 0x6d, 0xa0, 0x3c, 0xa3, 0x84, 0x24, 0x2d, 0xde, 0x4c, 0x5b, 0xbc, 0x79, 0x96, - 0xee, 0x11, 0x07, 0x05, 0x21, 0xe4, 0xc3, 0x7f, 0x54, 0x35, 0xbb, 0x28, 0xf3, 0x44, 0x44, 0x7f, - 0x00, 0x96, 0x5a, 0x51, 0x83, 0x44, 0x1e, 0x8e, 0x7c, 0x27, 0x46, 0x14, 0x13, 0xaf, 0x3c, 0x2b, - 0xa9, 0xd6, 0xae, 0x50, 0xd5, 0xd5, 0x6e, 0x92, 0x30, 0xfd, 0x52, 0x30, 0x2d, 0x66, 0xc9, 0xa7, - 0x32, 0x57, 0xff, 0x2e, 0xd0, 0x5d, 0xb7, 0x2d, 0x25, 0x91, 0x16, 0x4f, 0x19, 0x6f, 0x0d, 0xcf, - 0xb8, 0xe4, 0xba, 0xed, 0xb3, 0x24, 0x5b, 0x51, 0xfe, 0x10, 0xac, 0x72, 0x0a, 0x23, 0x76, 0x8e, - 0xe8, 0x65, 0xde, 0xc2, 0xf0, 0xbc, 0x77, 0x52, 0x8e, 0x5e, 0xf2, 0xfb, 0x60, 0x23, 0xeb, 0xcc, - 0x14, 0x79, 0x98, 0x71, 0x8a, 0x1b, 0x2d, 0xb9, 0xe8, 0xd2, 0x65, 0x53, 0x2e, 0xca, 0x87, 0xa0, - 0x92, 0xe2, 0xec, 0x1e, 0xd8, 0x37, 0x15, 0x4a, 0x3f, 0x01, 0xaf, 0xc8, 0x65, 0xca, 0x84, 0x38, - 0xa7, 0x87, 0x49, 0x4e, 0x1d, 0x62, 0xc6, 0x04, 0x1b, 0xd8, 0xd0, 0xb6, 0xa6, 0xec, 0xcd, 0x04, - 0x7b, 0x8a, 0x68, 0x3d, 0x87, 0x3c, 0xcb, 0x01, 0xf5, 0x6d, 0xa0, 0x37, 0x31, 0xe3, 0x84, 0x62, - 0x17, 0x06, 0x0e, 0x8a, 0x38, 0xc5, 0x88, 0x95, 0x4b, 0x32, 0x7d, 0xb9, 0x1b, 0xb9, 0x97, 0x04, - 0xf4, 0x77, 0xc1, 0xe6, 0xb5, 0x93, 0x3a, 0x6e, 0x13, 0x46, 0x11, 0x0a, 0xca, 0x73, 0xd2, 0x4a, - 0xd5, 0xbb, 0x66, 0xce, 0xc3, 0x04, 0xa6, 0xdf, 0x06, 0x33, 0x9c, 0xc4, 0xce, 0x83, 0xf2, 0xfc, - 0x86, 0xb6, 0x35, 0x6f, 0x4f, 0x73, 0x12, 0x3f, 0xd0, 0xdf, 0x00, 0x2b, 0x6d, 0x18, 0x60, 0x0f, - 0x72, 0x42, 0x99, 0x13, 0x93, 0x0b, 0x44, 0x1d, 0x17, 0xc6, 0xe5, 0x05, 0x89, 0xd1, 0xbb, 0xb1, - 0x53, 0x11, 0x3a, 0x84, 0xb1, 0x7e, 0x17, 0x2c, 0x67, 0xa3, 0x0e, 0x43, 0x5c, 0xc2, 0x17, 0x25, - 0x7c, 0x31, 0x0b, 0x3c, 0x44, 0x5c, 0x60, 0xd7, 0x41, 0x11, 0x06, 0x01, 0xb9, 0x08, 0x30, 0xe3, - 0xe5, 0xa5, 0x8d, 0xa9, 0xad, 0xa2, 0xdd, 0x1d, 0xd0, 0x0d, 0x50, 0xf0, 0x50, 0xd4, 0x91, 0xc1, - 0x65, 0x19, 0xcc, 0xae, 0x7b, 0xbb, 0x8e, 0x3e, 0x74, 0xd7, 0xb9, 0xd2, 0x2a, 0x5e, 0x06, 0x2f, - 0xf5, 0x69, 0x07, 0x59, 0xbb, 0xf8, 0x83, 0x06, 0xf4, 0x5c, 0xdc, 0x46, 0x21, 0x69, 0xc3, 0x60, - 0x50, 0xb7, 0xd8, 0x07, 0x45, 0x26, 0xca, 0x28, 0xd7, 0xe7, 0xe4, 0x08, 0xeb, 0xb3, 0x20, 0xd2, - 0xe4, 0xf2, 0xec, 0xf1, 0x36, 0x35, 0xbe, 0xb7, 0x75, 0x60, 0x5c, 0xd5, 0x9e, 0x59, 0xfb, 0xbd, - 0x06, 0xee, 0x88, 0x70, 0x13, 0x46, 0x3e, 0xb2, 0xd1, 0x05, 0xa4, 0x5e, 0x1d, 0x45, 0x24, 0x64, - 0x7a, 0x0d, 0xcc, 0x7b, 0xf2, 0x9f, 0xc3, 0x89, 0x38, 0xf2, 0x94, 0x35, 0x59, 0xfc, 0x52, 0x32, - 0x78, 0x46, 0xf6, 0x3d, 0x4f, 0xdf, 0x02, 0x4b, 0x5d, 0x0c, 0x15, 0xd4, 0xc2, 0xad, 0x80, 0x2d, - 0xa4, 0x30, 0x39, 0xe1, 0x7f, 0xcf, 0x4d, 0x55, 0x6e, 0xeb, 0x57, 0xe5, 0x66, 0x86, 0x9e, 0x68, - 0xa0, 0x70, 0xcc, 0xfc, 0x93, 0x98, 0x1f, 0x45, 0xff, 0xe7, 0x07, 0x3a, 0x1d, 0x2c, 0xa5, 0x4e, - 0x32, 0x7b, 0xbf, 0xd1, 0x40, 0x31, 0x19, 0x3c, 0x69, 0xf1, 0xff, 0x89, 0xbf, 0xae, 0xf8, 0xa9, - 0x9b, 0x88, 0xbf, 0x0d, 0x96, 0x33, 0x9d, 0x99, 0xfa, 0x3f, 0x69, 0xf2, 0x8c, 0x2a, 0xd6, 0xbd, - 0x2a, 0xd7, 0x21, 0x09, 0x55, 0x03, 0xb2, 0x21, 0x47, 0x57, 0x55, 0x6b, 0x43, 0xaa, 0xce, 0x57, - 0x62, 0xb2, 0xb7, 0x12, 0xf7, 0xc0, 0x34, 0x85, 0x1c, 0x29, 0x3b, 0x3b, 0x62, 0xa9, 0xfd, 0xfd, - 0x69, 0xf5, 0xa5, 0xc4, 0x12, 0xf3, 0x1e, 0x99, 0x98, 0x58, 0x21, 0xe4, 0x4d, 0xf3, 0x3b, 0xc8, - 0x87, 0x6e, 0xa7, 0x8e, 0xdc, 0x4f, 0x3f, 0xde, 0x06, 0xca, 0x71, 0x1d, 0xb9, 0xb6, 0x4c, 0xdf, - 0x2b, 0xa4, 0x2e, 0x6b, 0xaf, 0x82, 0x57, 0x06, 0x99, 0xc8, 0xdc, 0xfe, 0x65, 0x52, 0x9e, 0x40, - 0xb2, 0x73, 0x2a, 0xf1, 0xf0, 0xb9, 0x38, 0xee, 0x89, 0x0e, 0xbf, 0x02, 0x66, 0x38, 0xe6, 0x01, - 0x52, 0xb7, 0x2d, 0xb9, 0xd0, 0x37, 0x40, 0xc9, 0x43, 0xcc, 0xa5, 0x38, 0x96, 0xbb, 0x4f, 0x62, - 0x24, 0x3f, 0xd4, 0xe3, 0x73, 0xaa, 0xd7, 0x67, 0xd6, 0xb9, 0xa7, 0x87, 0xe8, 0xdc, 0x33, 0xa3, - 0x75, 0xee, 0xd9, 0x21, 0x3a, 0xf7, 0xad, 0x41, 0x9d, 0xbb, 0x30, 0xa8, 0x73, 0x17, 0x87, 0xee, - 0x07, 0xb5, 0x4d, 0x50, 0xbd, 0xa6, 0xa4, 0x69, 0xd9, 0x77, 0xff, 0x5c, 0x02, 0x53, 0xc7, 0xcc, - 0xd7, 0x7f, 0xa1, 0x81, 0xe5, 0xab, 0xef, 0x78, 0x5f, 0x1d, 0xea, 0x80, 0xd9, 0xef, 0x45, 0xca, - 0xd8, 0x1f, 0x3b, 0x35, 0xd5, 0xa6, 0xff, 0x4e, 0x03, 0xc6, 0x80, 0x17, 0xb0, 0x83, 0x61, 0x67, - 0xb8, 0x9e, 0xc3, 0x78, 0xf7, 0xe6, 0x1c, 0x03, 0xe4, 0xf6, 0xbc, 0x42, 0x8d, 0x29, 0x37, 0xcf, - 0x31, 0xae, 0xdc, 0x7e, 0x2f, 0x26, 0xfa, 0xcf, 0x35, 0xb0, 0x74, 0xe5, 0x4c, 0xff, 0x95, 0x61, - 0x27, 0xb8, 0x9c, 0x69, 0x7c, 0x63, 0xdc, 0xcc, 0x4c, 0xd0, 0x4f, 0x35, 0xb0, 0x78, 0xf9, 0xd4, - 0xf0, 0xf6, 0xa8, 0xac, 0x2a, 0xd1, 0xf8, 0xfa, 0x98, 0x89, 0x99, 0x9a, 0x0f, 0x34, 0x30, 0xd7, - 0xf3, 0xd2, 0xf6, 0xe5, 0x61, 0x19, 0xf3, 0x59, 0xc6, 0x3b, 0xe3, 0x64, 0x65, 0x22, 0x42, 0x30, - 0x93, 0xec, 0xcd, 0xdb, 0xc3, 0xd2, 0x48, 0xb8, 0xf1, 0xe6, 0x48, 0xf0, 0x6c, 0xba, 0x18, 0xcc, - 0xaa, 0xbd, 0xd2, 0x1c, 0x81, 0xe0, 0xa4, 0xc5, 0x8d, 0xb7, 0x46, 0xc3, 0x67, 0x33, 0xfe, 0x56, - 0x03, 0x6b, 0xd7, 0x6f, 0x70, 0x43, 0xf7, 0x90, 0x6b, 0x29, 0x8c, 0xa3, 0x1b, 0x53, 0x64, 0x5a, - 0x7f, 0xa5, 0x81, 0x95, 0xbe, 0xdb, 0xd3, 0x3b, 0xa3, 0x3e, 0x6b, 0xf9, 0x6c, 0xa3, 0x7e, 0x93, - 0xec, 0x54, 0x9c, 0x31, 0xf3, 0xe3, 0xcf, 0x1e, 0xdf, 0xd5, 0x0e, 0xbe, 0xff, 0xe4, 0x59, 0x45, - 0xfb, 0xe4, 0x59, 0x45, 0xfb, 0xe7, 0xb3, 0x8a, 0xf6, 0xe1, 0xf3, 0xca, 0xc4, 0x27, 0xcf, 0x2b, - 0x13, 0x7f, 0x7b, 0x5e, 0x99, 0xf8, 0xc1, 0xd7, 0x7c, 0xcc, 0x9b, 0xad, 0x86, 0xe9, 0x92, 0x50, - 0x7d, 0xa0, 0xb4, 0xba, 0xf3, 0x6e, 0x67, 0xdf, 0x17, 0xdb, 0x6f, 0x5a, 0xef, 0xf7, 0x7e, 0x64, - 0x94, 0x1f, 0x65, 0x1a, 0xb3, 0xf2, 0x20, 0xfe, 0xa5, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x39, - 0xec, 0x0a, 0x75, 0xe0, 0x15, 0x00, 0x00, + // 1764 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xcd, 0x8f, 0x1c, 0x47, + 0x15, 0xdf, 0xf6, 0x7e, 0x78, 0xe6, 0xcd, 0x7e, 0xf6, 0xae, 0xd9, 0xd9, 0xb6, 0x33, 0xb3, 0x5e, + 0x42, 0xb2, 0x32, 0xd9, 0xee, 0xd8, 0x90, 0x04, 0x56, 0x41, 0xb0, 0x1f, 0x06, 0x6f, 0x60, 0xed, + 0xa5, 0x6d, 0x8c, 0x04, 0x12, 0xad, 0x9a, 0xee, 0x72, 0x4f, 0xc9, 0xdd, 0x5d, 0xad, 0xae, 0x9a, + 0x71, 0xe6, 0x16, 0xe5, 0x84, 0x84, 0x84, 0x82, 0xc4, 0x01, 0x71, 0xca, 0x01, 0x21, 0x21, 0x01, + 0xf2, 0x21, 0x27, 0x6e, 0xdc, 0x7c, 0x8c, 0x22, 0x0e, 0x28, 0x07, 0x83, 0xec, 0x43, 0x38, 0xf3, + 0x17, 0xa0, 0xaa, 0xae, 0xee, 0xe9, 0xd9, 0x99, 0x9d, 0xf4, 0x8e, 0xc3, 0x21, 0x97, 0xd1, 0x74, + 0xbd, 0xdf, 0xfb, 0xd5, 0xef, 0xbd, 0xea, 0x7a, 0xaf, 0xaa, 0xe1, 0x35, 0x12, 0x71, 0x9c, 0xb8, + 0x6d, 0x44, 0x22, 0x87, 0x61, 0xb7, 0x93, 0x10, 0xde, 0xb3, 0x5c, 0xb7, 0x6b, 0xc5, 0x09, 0xed, + 0x12, 0x0f, 0x27, 0x56, 0xf7, 0xba, 0xc5, 0xdf, 0x35, 0xe3, 0x84, 0x72, 0xaa, 0x7f, 0x75, 0x04, + 0xda, 0x74, 0xdd, 0xae, 0x99, 0xa1, 0xcd, 0xee, 0x75, 0x63, 0x05, 0x85, 0x24, 0xa2, 0x96, 0xfc, + 0x4d, 0xfd, 0x8c, 0x2b, 0x3e, 0xa5, 0x7e, 0x80, 0x2d, 0x14, 0x13, 0x0b, 0x45, 0x11, 0xe5, 0x88, + 0x13, 0x1a, 0x31, 0x65, 0x6d, 0x2a, 0xab, 0x7c, 0x6a, 0x75, 0x1e, 0x58, 0x9c, 0x84, 0x98, 0x71, + 0x14, 0xc6, 0x0a, 0xd0, 0x38, 0x0d, 0xf0, 0x3a, 0x89, 0x64, 0x50, 0xf6, 0x8d, 0xd3, 0x76, 0x14, + 0xf5, 0x94, 0x69, 0xcd, 0xa7, 0x3e, 0x95, 0x7f, 0x2d, 0xf1, 0x2f, 0x73, 0x70, 0x29, 0x0b, 0x29, + 0x73, 0x52, 0x43, 0xfa, 0xa0, 0x4c, 0xeb, 0xe9, 0x93, 0x15, 0x32, 0x5f, 0x84, 0x1e, 0x32, 0x3f, + 0x53, 0x49, 0x5a, 0xae, 0xe5, 0xd2, 0x04, 0x5b, 0x6e, 0x40, 0x70, 0xc4, 0x85, 0x35, 0xfd, 0xa7, + 0x00, 0x37, 0xca, 0xa4, 0x32, 0x4f, 0x54, 0xea, 0x63, 0x09, 0xd2, 0x80, 0xf8, 0x6d, 0x9e, 0x52, + 0x31, 0x8b, 0xe3, 0xc8, 0xc3, 0x49, 0x48, 0xd2, 0x09, 0xfa, 0x4f, 0x99, 0x8a, 0x82, 0x9d, 0xf7, + 0x62, 0xcc, 0x2c, 0x2c, 0xf8, 0x22, 0x17, 0xa7, 0x80, 0xad, 0x7f, 0x68, 0xb0, 0x76, 0xcc, 0xfc, + 0x3d, 0xc6, 0x88, 0x1f, 0x1d, 0xd0, 0x88, 0x75, 0x42, 0x9c, 0xfc, 0x10, 0xf7, 0xf4, 0x0d, 0xa8, + 0xa4, 0xda, 0x88, 0x57, 0xd7, 0x36, 0xb5, 0xed, 0xaa, 0x7d, 0x51, 0x3e, 0x1f, 0x79, 0xfa, 0x5b, + 0xb0, 0x90, 0xe9, 0x72, 0x90, 0xe7, 0x25, 0xf5, 0x0b, 0xc2, 0xbe, 0xaf, 0xff, 0xf7, 0x69, 0x73, + 0xb1, 0x87, 0xc2, 0x60, 0x77, 0x4b, 0x8c, 0x62, 0xc6, 0xb6, 0xec, 0xf9, 0x0c, 0xb8, 0xe7, 0x79, + 0x89, 0x7e, 0x15, 0xe6, 0x5d, 0x35, 0x85, 0xf3, 0x10, 0xf7, 0xea, 0xd3, 0x92, 0xb7, 0xe6, 0x16, + 0xa6, 0x7d, 0x1d, 0xe6, 0x84, 0x12, 0x9c, 0xd4, 0x67, 0x24, 0x69, 0xfd, 0x93, 0x8f, 0x76, 0xd6, + 0x54, 0xc6, 0xf7, 0x52, 0xd6, 0xbb, 0x3c, 0x21, 0x91, 0x6f, 0x2b, 0xdc, 0xee, 0xea, 0x2f, 0x3f, + 0x6c, 0x4e, 0xfd, 0xe7, 0xc3, 0xe6, 0xd4, 0xfb, 0x9f, 0x3d, 0xbe, 0xa6, 0x06, 0xb7, 0x1a, 0x70, + 0x65, 0x54, 0x54, 0x36, 0x66, 0x31, 0x8d, 0x18, 0xde, 0xfa, 0xbb, 0x06, 0x2f, 0x1d, 0x33, 0xff, + 0x6e, 0xa7, 0x15, 0x12, 0x9e, 0x01, 0x8e, 0x09, 0x6b, 0xe1, 0x36, 0xea, 0x12, 0xda, 0x49, 0xf4, + 0x37, 0xa1, 0xca, 0xa4, 0x95, 0xe3, 0x24, 0x4d, 0xc0, 0x18, 0x2d, 0x7d, 0xa8, 0x7e, 0x02, 0xf3, + 0x61, 0x81, 0x47, 0xe6, 0xa6, 0x76, 0xe3, 0x35, 0x93, 0xb4, 0x5c, 0xb3, 0xb8, 0x72, 0x66, 0x61, + 0xad, 0xba, 0xd7, 0xcd, 0xe2, 0xdc, 0xf6, 0x00, 0xc3, 0xee, 0x57, 0x8a, 0x01, 0xf6, 0x67, 0xda, + 0x7a, 0x15, 0xbe, 0x36, 0x36, 0x84, 0x3c, 0xd8, 0xc7, 0x17, 0x46, 0x04, 0x7b, 0x48, 0x3b, 0xad, + 0x00, 0xdf, 0xa7, 0x9c, 0x44, 0xfe, 0xc4, 0xc1, 0x3a, 0xb0, 0xee, 0x75, 0xe2, 0x80, 0xb8, 0x88, + 0x63, 0xa7, 0x4b, 0x39, 0x76, 0xb2, 0xd7, 0x4b, 0xc5, 0xfd, 0x6a, 0x31, 0x4c, 0xf9, 0x02, 0x9a, + 0x87, 0x99, 0xc3, 0x7d, 0xca, 0xf1, 0x4d, 0x05, 0xb7, 0x2f, 0x79, 0xa3, 0x86, 0xf5, 0x5f, 0xc0, + 0x3a, 0x89, 0x1e, 0x24, 0xc8, 0x15, 0xdb, 0xd7, 0x69, 0x05, 0xd4, 0x7d, 0xe8, 0xb4, 0x31, 0xf2, + 0x70, 0x22, 0x5f, 0x9e, 0xda, 0x8d, 0x57, 0x3e, 0x2f, 0xb1, 0xb7, 0x24, 0xda, 0xbe, 0xd4, 0xa7, + 0xd9, 0x17, 0x2c, 0xe9, 0xf0, 0xb9, 0x72, 0x5b, 0xcc, 0x58, 0x9e, 0xdb, 0x3f, 0x68, 0xb0, 0x74, + 0xcc, 0xfc, 0x9f, 0xc4, 0x1e, 0xe2, 0xf8, 0x04, 0x25, 0x28, 0x64, 0x22, 0x9b, 0xa8, 0xc3, 0xdb, + 0x54, 0xec, 0xe8, 0xcf, 0xcf, 0x66, 0x0e, 0xd5, 0x8f, 0x60, 0x2e, 0x96, 0x0c, 0x2a, 0x79, 0x5f, + 0x37, 0x4b, 0xd4, 0x4f, 0x33, 0x9d, 0x74, 0x7f, 0xe6, 0xc9, 0xd3, 0xe6, 0x94, 0xad, 0x08, 0x76, + 0x17, 0x65, 0x3c, 0x39, 0xf5, 0xd6, 0x06, 0xac, 0x9f, 0x52, 0x99, 0x47, 0xf0, 0x69, 0x05, 0x56, + 0x8f, 0x99, 0x9f, 0x45, 0xb9, 0xe7, 0x79, 0x44, 0x64, 0x69, 0x5c, 0x01, 0xf8, 0x01, 0x2c, 0x92, + 0x88, 0x70, 0x82, 0x02, 0xa7, 0x8d, 0x45, 0xea, 0x95, 0x60, 0x43, 0x2e, 0x86, 0x28, 0x7a, 0xa6, + 0x2a, 0x75, 0x72, 0x01, 0x04, 0x42, 0xe9, 0x5b, 0x50, 0x7e, 0xe9, 0xa0, 0x28, 0x08, 0x3e, 0x8e, + 0x30, 0x23, 0xcc, 0x69, 0x23, 0xd6, 0x96, 0x6b, 0x3a, 0x6f, 0xd7, 0xd4, 0xd8, 0x2d, 0xc4, 0xda, + 0x7a, 0x13, 0x6a, 0x2d, 0x12, 0xa1, 0xa4, 0x97, 0x22, 0x66, 0x24, 0x02, 0xd2, 0x21, 0x09, 0x38, + 0x00, 0x60, 0x31, 0x7a, 0x14, 0x39, 0xa2, 0x0d, 0xd4, 0x67, 0x95, 0x90, 0xb4, 0xc4, 0x9b, 0x59, + 0x89, 0x37, 0xef, 0x65, 0x3d, 0x62, 0xbf, 0x22, 0x84, 0x7c, 0xf0, 0xaf, 0xa6, 0x66, 0x57, 0xa5, + 0x9f, 0xb0, 0xe8, 0xb7, 0x61, 0xb9, 0x13, 0xb5, 0x68, 0xe4, 0x91, 0xc8, 0x77, 0x62, 0x9c, 0x10, + 0xea, 0xd5, 0xe7, 0x24, 0xd5, 0xc6, 0x10, 0xd5, 0xa1, 0xea, 0x26, 0x29, 0xd3, 0xef, 0x04, 0xd3, + 0x52, 0xee, 0x7c, 0x22, 0x7d, 0xf5, 0x1f, 0x83, 0xee, 0xba, 0x5d, 0x29, 0x89, 0x76, 0x78, 0xc6, + 0x78, 0xb1, 0x3c, 0xe3, 0xb2, 0xeb, 0x76, 0xef, 0xa5, 0xde, 0x8a, 0xf2, 0xe7, 0xb0, 0xce, 0x13, + 0x14, 0xb1, 0x07, 0x38, 0x39, 0xcd, 0x5b, 0x29, 0xcf, 0x7b, 0x29, 0xe3, 0x18, 0x24, 0xbf, 0x05, + 0x9b, 0x79, 0x65, 0x4e, 0xb0, 0x47, 0x18, 0x4f, 0x48, 0xab, 0x23, 0x37, 0x5d, 0xb6, 0x6d, 0xea, + 0x55, 0xf9, 0x12, 0x34, 0x32, 0x9c, 0x3d, 0x00, 0xfb, 0xbe, 0x42, 0xe9, 0x77, 0xe0, 0x65, 0xb9, + 0x4d, 0x99, 0x10, 0xe7, 0x0c, 0x30, 0xc9, 0xa9, 0x43, 0xc2, 0x98, 0x60, 0x83, 0x4d, 0x6d, 0x7b, + 0xda, 0xbe, 0x9a, 0x62, 0x4f, 0x70, 0x72, 0x58, 0x40, 0xde, 0x2b, 0x00, 0xf5, 0x1d, 0xd0, 0xdb, + 0x84, 0x71, 0x9a, 0x10, 0x17, 0x05, 0x0e, 0x8e, 0x78, 0x42, 0x30, 0xab, 0xd7, 0xa4, 0xfb, 0x4a, + 0xdf, 0x72, 0x33, 0x35, 0xe8, 0xef, 0xc0, 0xd5, 0x33, 0x27, 0x75, 0xdc, 0x36, 0x8a, 0x22, 0x1c, + 0xd4, 0xe7, 0x65, 0x28, 0x4d, 0xef, 0x8c, 0x39, 0x0f, 0x52, 0x98, 0xbe, 0x0a, 0xb3, 0x9c, 0xc6, + 0xce, 0xed, 0xfa, 0xc2, 0xa6, 0xb6, 0xbd, 0x60, 0xcf, 0x70, 0x1a, 0xdf, 0xd6, 0x5f, 0x87, 0xb5, + 0x2e, 0x0a, 0x88, 0x87, 0x38, 0x4d, 0x98, 0x13, 0xd3, 0x47, 0x38, 0x71, 0x5c, 0x14, 0xd7, 0x17, + 0x25, 0x46, 0xef, 0xdb, 0x4e, 0x84, 0xe9, 0x00, 0xc5, 0xfa, 0x35, 0x58, 0xc9, 0x47, 0x1d, 0x86, + 0xb9, 0x84, 0x2f, 0x49, 0xf8, 0x52, 0x6e, 0xb8, 0x8b, 0xb9, 0xc0, 0x5e, 0x81, 0x2a, 0x0a, 0x02, + 0xfa, 0x28, 0x20, 0x8c, 0xd7, 0x97, 0x37, 0xa7, 0xb7, 0xab, 0x76, 0x7f, 0x40, 0x37, 0xa0, 0xe2, + 0xe1, 0xa8, 0x27, 0x8d, 0x2b, 0xd2, 0x98, 0x3f, 0x0f, 0x56, 0x1d, 0xbd, 0x7c, 0xd5, 0xb9, 0x0c, + 0xd5, 0x50, 0xd4, 0x17, 0x8e, 0x1e, 0xe2, 0xfa, 0xea, 0xa6, 0xb6, 0x3d, 0x63, 0x57, 0x42, 0x12, + 0xdd, 0x15, 0xcf, 0xba, 0x09, 0xab, 0x72, 0x76, 0x87, 0x44, 0x62, 0x7d, 0xbb, 0xd8, 0xe9, 0xa2, + 0x80, 0xd5, 0xd7, 0x36, 0xb5, 0xed, 0x8a, 0xbd, 0x22, 0x4d, 0x47, 0xca, 0x72, 0x1f, 0x05, 0xc3, + 0x75, 0xe7, 0x25, 0xb8, 0x3c, 0xa2, 0xb6, 0xe4, 0xb5, 0xe7, 0x6f, 0x1a, 0xe8, 0x05, 0xbb, 0x8d, + 0x43, 0xda, 0x45, 0xc1, 0xb8, 0xd2, 0xb3, 0x07, 0x55, 0x26, 0xd6, 0x44, 0x6e, 0xf6, 0x0b, 0xe7, + 0xd8, 0xec, 0x15, 0xe1, 0x26, 0xf7, 0xfa, 0x40, 0xa2, 0xa6, 0x4b, 0x27, 0x6a, 0x28, 0xb6, 0x2b, + 0x60, 0x0c, 0x6b, 0xcf, 0x43, 0xfb, 0x8b, 0x06, 0x97, 0x84, 0xb9, 0x8d, 0x22, 0x1f, 0xdb, 0xf8, + 0x11, 0x4a, 0xbc, 0x43, 0x1c, 0xd1, 0x90, 0xe9, 0x5b, 0xb0, 0xe0, 0xc9, 0x7f, 0x0e, 0xa7, 0xe2, + 0xfc, 0x54, 0xd7, 0xe4, 0x4a, 0xd6, 0xd2, 0xc1, 0x7b, 0x74, 0xcf, 0xf3, 0xf4, 0x6d, 0x58, 0xee, + 0x63, 0x12, 0x41, 0x2d, 0xa2, 0x15, 0xb0, 0xc5, 0x0c, 0x26, 0x27, 0xfc, 0xe2, 0xa2, 0x69, 0xca, + 0x33, 0xc2, 0xb0, 0xdc, 0x3c, 0xa0, 0x27, 0x1a, 0x54, 0x8e, 0x99, 0x7f, 0x27, 0xe6, 0x47, 0xd1, + 0x97, 0xfc, 0x74, 0xa8, 0xc3, 0x72, 0x16, 0x49, 0x1e, 0xde, 0x1f, 0x35, 0xa8, 0xa6, 0x83, 0x77, + 0x3a, 0xfc, 0xff, 0x12, 0x5f, 0x5f, 0xfc, 0xf4, 0x8b, 0x88, 0x5f, 0x85, 0x95, 0x5c, 0x67, 0xae, + 0xfe, 0xbd, 0x0b, 0xf2, 0xc0, 0x2b, 0x8a, 0x88, 0x4a, 0xd7, 0x01, 0x0d, 0x55, 0x35, 0xb3, 0x11, + 0xc7, 0xc3, 0xaa, 0xb5, 0x92, 0xaa, 0x8b, 0x99, 0xb8, 0x30, 0x98, 0x89, 0x9b, 0x30, 0x93, 0x20, + 0x8e, 0x55, 0x38, 0xd7, 0xc5, 0x56, 0xfb, 0xf4, 0x69, 0xf3, 0x72, 0x1a, 0x12, 0xf3, 0x1e, 0x9a, + 0x84, 0x5a, 0x21, 0xe2, 0x6d, 0xf3, 0x47, 0xd8, 0x47, 0x6e, 0xef, 0x10, 0xbb, 0x9f, 0x7c, 0xb4, + 0x03, 0x2a, 0xe2, 0x43, 0xec, 0xda, 0xd2, 0xfd, 0x8b, 0x5a, 0xd4, 0x57, 0xe0, 0xe5, 0x71, 0x19, + 0xc8, 0x53, 0xf5, 0xd7, 0x69, 0x79, 0x16, 0xca, 0x4f, 0xcc, 0xd4, 0x23, 0x0f, 0xc4, 0xc1, 0x53, + 0xf4, 0x9a, 0x35, 0x98, 0xe5, 0x84, 0x07, 0x58, 0xad, 0x79, 0xfa, 0xa0, 0x6f, 0x42, 0xcd, 0xc3, + 0xcc, 0x4d, 0x48, 0x2c, 0xfb, 0x60, 0x9a, 0x85, 0xe2, 0xd0, 0x40, 0x92, 0xa6, 0x07, 0x93, 0x94, + 0xf7, 0x90, 0x99, 0x12, 0x3d, 0x64, 0xf6, 0x7c, 0x3d, 0x64, 0xae, 0x44, 0x0f, 0xb9, 0x38, 0xae, + 0x87, 0x54, 0xc6, 0xf5, 0x90, 0xea, 0x84, 0x3d, 0x04, 0xca, 0xf5, 0x90, 0x5a, 0xd9, 0x1e, 0x72, + 0x15, 0x9a, 0x67, 0xac, 0x57, 0xb6, 0xa6, 0x37, 0x9e, 0xcf, 0xc3, 0xf4, 0x31, 0xf3, 0xf5, 0xdf, + 0x68, 0xb0, 0x32, 0x7c, 0x95, 0xfd, 0x76, 0xa9, 0x73, 0xf4, 0xa8, 0xfb, 0xa2, 0xb1, 0x37, 0xb1, + 0x6b, 0xa6, 0x4d, 0xff, 0xb3, 0x06, 0xc6, 0x98, 0x7b, 0xe6, 0x7e, 0xd9, 0x19, 0xce, 0xe6, 0x30, + 0xde, 0x79, 0x71, 0x8e, 0x31, 0x72, 0x07, 0x6e, 0x8a, 0x13, 0xca, 0x2d, 0x72, 0x4c, 0x2a, 0x77, + 0xd4, 0xfd, 0x4b, 0xff, 0xb5, 0x06, 0xcb, 0x43, 0x57, 0x97, 0x6f, 0x95, 0x9d, 0xe0, 0xb4, 0xa7, + 0xf1, 0xbd, 0x49, 0x3d, 0x73, 0x41, 0xbf, 0xd2, 0x60, 0xe9, 0xf4, 0x79, 0xe6, 0xad, 0xf3, 0xb2, + 0x2a, 0x47, 0xe3, 0xbb, 0x13, 0x3a, 0xe6, 0x6a, 0xde, 0xd7, 0x60, 0x7e, 0xe0, 0x6e, 0xfa, 0xcd, + 0xb2, 0x8c, 0x45, 0x2f, 0xe3, 0xed, 0x49, 0xbc, 0x72, 0x11, 0x21, 0xcc, 0xa6, 0xa7, 0x86, 0x9d, + 0xb2, 0x34, 0x12, 0x6e, 0xbc, 0x71, 0x2e, 0x78, 0x3e, 0x5d, 0x0c, 0x73, 0xaa, 0x8b, 0x9b, 0xe7, + 0x20, 0xb8, 0xd3, 0xe1, 0xc6, 0x9b, 0xe7, 0xc3, 0xe7, 0x33, 0xfe, 0x49, 0x83, 0x8d, 0xb3, 0x5b, + 0x6f, 0xe9, 0x1a, 0x72, 0x26, 0x85, 0x71, 0xf4, 0xc2, 0x14, 0xb9, 0xd6, 0xdf, 0x6b, 0xb0, 0x36, + 0xb2, 0xf7, 0xbd, 0x7d, 0xde, 0x77, 0xad, 0xe8, 0x6d, 0x1c, 0xbe, 0x88, 0x77, 0x2e, 0xee, 0xb7, + 0x1a, 0xe8, 0x23, 0x4e, 0xcc, 0xbb, 0xa5, 0xc9, 0x87, 0x7c, 0x8d, 0xfd, 0xc9, 0x7d, 0x33, 0x59, + 0xc6, 0xec, 0x7b, 0x9f, 0x3d, 0xbe, 0xa6, 0xed, 0xff, 0xf4, 0xc9, 0xb3, 0x86, 0xf6, 0xf1, 0xb3, + 0x86, 0xf6, 0xef, 0x67, 0x0d, 0xed, 0x83, 0xe7, 0x8d, 0xa9, 0x8f, 0x9f, 0x37, 0xa6, 0xfe, 0xf9, + 0xbc, 0x31, 0xf5, 0xb3, 0xef, 0xf8, 0x84, 0xb7, 0x3b, 0x2d, 0xd3, 0xa5, 0xa1, 0xfa, 0x3c, 0x6c, + 0xf5, 0x67, 0xdd, 0xc9, 0xbf, 0xee, 0x76, 0xdf, 0xb0, 0xde, 0x1d, 0xfc, 0xc4, 0x2b, 0x3f, 0x89, + 0xb5, 0xe6, 0xe4, 0xcd, 0xe5, 0x1b, 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x88, 0x80, 0xb2, 0x2a, + 0x5e, 0x17, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1426,6 +1469,7 @@ type MsgClient interface { OptOut(ctx context.Context, in *MsgOptOut, opts ...grpc.CallOption) (*MsgOptOutResponse, error) SetConsumerCommissionRate(ctx context.Context, in *MsgSetConsumerCommissionRate, opts ...grpc.CallOption) (*MsgSetConsumerCommissionRateResponse, error) ConsumerModification(ctx context.Context, in *MsgConsumerModification, opts ...grpc.CallOption) (*MsgConsumerModificationResponse, error) + ChangeRewardDenoms(ctx context.Context, in *MsgChangeRewardDenoms, opts ...grpc.CallOption) (*MsgChangeRewardDenomsResponse, error) } type msgClient struct { @@ -1526,6 +1570,15 @@ func (c *msgClient) ConsumerModification(ctx context.Context, in *MsgConsumerMod return out, nil } +func (c *msgClient) ChangeRewardDenoms(ctx context.Context, in *MsgChangeRewardDenoms, opts ...grpc.CallOption) (*MsgChangeRewardDenomsResponse, error) { + out := new(MsgChangeRewardDenomsResponse) + err := c.cc.Invoke(ctx, "/interchain_security.ccv.provider.v1.Msg/ChangeRewardDenoms", 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) @@ -1538,6 +1591,7 @@ type MsgServer interface { OptOut(context.Context, *MsgOptOut) (*MsgOptOutResponse, error) SetConsumerCommissionRate(context.Context, *MsgSetConsumerCommissionRate) (*MsgSetConsumerCommissionRateResponse, error) ConsumerModification(context.Context, *MsgConsumerModification) (*MsgConsumerModificationResponse, error) + ChangeRewardDenoms(context.Context, *MsgChangeRewardDenoms) (*MsgChangeRewardDenomsResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -1574,6 +1628,9 @@ func (*UnimplementedMsgServer) SetConsumerCommissionRate(ctx context.Context, re func (*UnimplementedMsgServer) ConsumerModification(ctx context.Context, req *MsgConsumerModification) (*MsgConsumerModificationResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ConsumerModification not implemented") } +func (*UnimplementedMsgServer) ChangeRewardDenoms(ctx context.Context, req *MsgChangeRewardDenoms) (*MsgChangeRewardDenomsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ChangeRewardDenoms not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -1759,6 +1816,24 @@ func _Msg_ConsumerModification_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } +func _Msg_ChangeRewardDenoms_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgChangeRewardDenoms) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ChangeRewardDenoms(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/interchain_security.ccv.provider.v1.Msg/ChangeRewardDenoms", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ChangeRewardDenoms(ctx, req.(*MsgChangeRewardDenoms)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "interchain_security.ccv.provider.v1.Msg", HandlerType: (*MsgServer)(nil), @@ -1803,6 +1878,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "ConsumerModification", Handler: _Msg_ConsumerModification_Handler, }, + { + MethodName: "ChangeRewardDenoms", + Handler: _Msg_ChangeRewardDenoms_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "interchain_security/ccv/provider/v1/tx.proto", @@ -2107,6 +2186,25 @@ func (m *MsgConsumerAddition) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.AllowInactiveVals { + i-- + if m.AllowInactiveVals { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa0 + } + if m.MinStake != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.MinStake)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x98 + } if len(m.Authority) > 0 { i -= len(m.Authority) copy(dAtA[i:], m.Authority) @@ -2566,6 +2664,13 @@ func (m *MsgSetConsumerCommissionRate) MarshalToSizedBuffer(dAtA []byte) (int, e _ = i var l int _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x22 + } { size := m.Rate.Size() i -= size @@ -2636,6 +2741,21 @@ func (m *MsgConsumerModification) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l + if m.AllowInactiveVals { + i-- + if m.AllowInactiveVals { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x58 + } + if m.MinStake != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.MinStake)) + i-- + dAtA[i] = 0x50 + } if len(m.Authority) > 0 { i -= len(m.Authority) copy(dAtA[i:], m.Authority) @@ -2915,6 +3035,12 @@ func (m *MsgConsumerAddition) Size() (n int) { if l > 0 { n += 2 + l + sovTx(uint64(l)) } + if m.MinStake != 0 { + n += 2 + sovTx(uint64(m.MinStake)) + } + if m.AllowInactiveVals { + n += 3 + } return n } @@ -3069,6 +3195,10 @@ func (m *MsgSetConsumerCommissionRate) Size() (n int) { } l = m.Rate.Size() n += 1 + l + sovTx(uint64(l)) + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } return n } @@ -3124,6 +3254,12 @@ func (m *MsgConsumerModification) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } + if m.MinStake != 0 { + n += 1 + sovTx(uint64(m.MinStake)) + } + if m.AllowInactiveVals { + n += 2 + } return n } @@ -4456,6 +4592,45 @@ func (m *MsgConsumerAddition) Unmarshal(dAtA []byte) error { } m.Authority = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 19: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MinStake", wireType) + } + m.MinStake = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MinStake |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 20: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowInactiveVals", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowInactiveVals = bool(v != 0) default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -5471,6 +5646,38 @@ func (m *MsgSetConsumerCommissionRate) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -5820,6 +6027,45 @@ func (m *MsgConsumerModification) Unmarshal(dAtA []byte) error { } m.Authority = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MinStake", wireType) + } + m.MinStake = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MinStake |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowInactiveVals", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowInactiveVals = bool(v != 0) default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) diff --git a/x/ccv/types/events.go b/x/ccv/types/events.go index 4c597ded56..3c63fd7954 100644 --- a/x/ccv/types/events.go +++ b/x/ccv/types/events.go @@ -15,7 +15,6 @@ const ( EventTypeExecuteConsumerChainSlash = "execute_consumer_chain_slash" EventTypeFeeDistribution = "fee_distribution" EventTypeConsumerSlashRequest = "consumer_slash_request" - EventTypeVSCMatured = "vsc_matured" AttributeKeyAckSuccess = "success" AttributeKeyAck = "acknowledgement" diff --git a/x/ccv/types/expected_keepers.go b/x/ccv/types/expected_keepers.go index fbfbd61111..5773461c34 100644 --- a/x/ccv/types/expected_keepers.go +++ b/x/ccv/types/expected_keepers.go @@ -4,6 +4,8 @@ import ( context "context" "time" + addresscodec "cosmossdk.io/core/address" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" conntypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" @@ -12,12 +14,11 @@ import ( "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" - abci "github.com/cometbft/cometbft/abci/types" ) @@ -55,6 +56,18 @@ type StakingKeeper interface { GetUnbondingDelegationByUnbondingID(ctx context.Context, id uint64) (stakingtypes.UnbondingDelegation, error) GetRedelegationByUnbondingID(ctx context.Context, id uint64) (stakingtypes.Redelegation, error) GetValidatorByUnbondingID(ctx context.Context, id uint64) (stakingtypes.Validator, error) + GetBondedValidatorsByPower(ctx context.Context) ([]stakingtypes.Validator, error) + ValidatorAddressCodec() addresscodec.Codec + IterateDelegations( + ctx context.Context, delegator sdk.AccAddress, + fn func(index int64, delegation stakingtypes.DelegationI) (stop bool), + ) error + IterateBondedValidatorsByPower( + context.Context, func(index int64, validator stakingtypes.ValidatorI) (stop bool), + ) error + StakingTokenSupply(ctx context.Context) (math.Int, error) + BondedRatio(ctx context.Context) (math.LegacyDec, error) + TotalBondedTokens(ctx context.Context) (math.Int, error) } // SlashingKeeper defines the contract expected to perform ccv slashing diff --git a/x/ccv/types/utils.go b/x/ccv/types/utils.go index cb174168e8..05e2387eb8 100644 --- a/x/ccv/types/utils.go +++ b/x/ccv/types/utils.go @@ -125,45 +125,21 @@ func GetConsAddrFromBech32(bech32str string) (sdk.ConsAddress, error) { return sdk.ConsAddress(addr), nil } -func GetLastBondedValidatorsUtil(ctx sdk.Context, stakingKeeper StakingKeeper, logger log.Logger) ([]stakingtypes.Validator, error) { - maxVals, err := stakingKeeper.MaxValidators(ctx) +// GetLastBondedValidatorsUtil iterates the last validator powers in the staking module +// and returns the first maxVals many validators with the largest powers. +func GetLastBondedValidatorsUtil(ctx sdk.Context, stakingKeeper StakingKeeper, logger log.Logger, maxVals uint32) ([]stakingtypes.Validator, error) { + // get the bonded validators from the staking module, sorted by power + bondedValidators, err := stakingKeeper.GetBondedValidatorsByPower(ctx) if err != nil { return nil, err } - lastPowers := make([]stakingtypes.LastValidatorPower, maxVals) - - i := 0 - err = 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 - }) - - if err != nil { - return nil, err + // get the first maxVals many validators + if uint32(len(bondedValidators)) < maxVals { + return bondedValidators, nil // no need to truncate } - // truncate the lastPowers - lastPowers = lastPowers[:i] - - bondedValidators := make([]stakingtypes.Validator, len(lastPowers)) + bondedValidators = bondedValidators[:maxVals] - 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, err := stakingKeeper.GetValidator(ctx, addr) - if err != nil { - logger.Error(err.Error(), 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, nil }