diff --git a/.changelog/unreleased/dependencies/2390-bump-sdk.md b/.changelog/unreleased/dependencies/2390-bump-sdk.md deleted file mode 100644 index 01600fdec9..0000000000 --- a/.changelog/unreleased/dependencies/2390-bump-sdk.md +++ /dev/null @@ -1,3 +0,0 @@ -- Bump [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) to - [v0.50.10](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.10) - ([\#2390](https://github.com/cosmos/interchain-security/pull/2390)) \ No newline at end of file diff --git a/.changelog/unreleased/state-breaking/2390-bump-sdk.md b/.changelog/unreleased/state-breaking/2390-bump-sdk.md deleted file mode 100644 index 01600fdec9..0000000000 --- a/.changelog/unreleased/state-breaking/2390-bump-sdk.md +++ /dev/null @@ -1,3 +0,0 @@ -- Bump [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) to - [v0.50.10](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.10) - ([\#2390](https://github.com/cosmos/interchain-security/pull/2390)) \ No newline at end of file diff --git a/.changelog/v6.4.0/api-breaking/2400-preccv.md b/.changelog/v6.4.0/api-breaking/2400-preccv.md new file mode 100644 index 0000000000..2714f1e522 --- /dev/null +++ b/.changelog/v6.4.0/api-breaking/2400-preccv.md @@ -0,0 +1,12 @@ +- Enable existing (standalone) chains to use the existing client (and connection) + to the provider chain when becoming a consumer chain. This feature introduces + the following API-breaking changes. + ([\#2400](https://github.com/cosmos/interchain-security/pull/2400)) + + - Add `connection_id` and `preCCV` to `ConsumerGenesisState`, the consumer + genesis state created by the provider chain. If the `connection_id` is not empty, + `preCCV` is set to true and both `provider.client_state` and `provider.consensus_state` + are set to nil (as the consumer doesn't need to create a new provider client). + As a result, for older versions of consumers, the `connection_id` in + `ConsumerInitializationParameters` must be empty and the resulting `ConsumerGenesisState` + needs to be adapted, i.e., both `connection_id` and `preCCV` need to be removed. \ No newline at end of file diff --git a/.changelog/unreleased/bug-fixes/2373-consumer-backward-compatibility.md b/.changelog/v6.4.0/bug-fixes/2373-consumer-backward-compatibility.md similarity index 100% rename from .changelog/unreleased/bug-fixes/2373-consumer-backward-compatibility.md rename to .changelog/v6.4.0/bug-fixes/2373-consumer-backward-compatibility.md diff --git a/.changelog/unreleased/bug-fixes/2377-fix-list-consumer-chains-pagination.md b/.changelog/v6.4.0/bug-fixes/2377-fix-list-consumer-chains-pagination.md similarity index 100% rename from .changelog/unreleased/bug-fixes/2377-fix-list-consumer-chains-pagination.md rename to .changelog/v6.4.0/bug-fixes/2377-fix-list-consumer-chains-pagination.md diff --git a/.changelog/unreleased/bug-fixes/2398-enable-pagination-in-listing-chain-query.md b/.changelog/v6.4.0/bug-fixes/2398-enable-pagination-in-listing-chain-query.md similarity index 100% rename from .changelog/unreleased/bug-fixes/2398-enable-pagination-in-listing-chain-query.md rename to .changelog/v6.4.0/bug-fixes/2398-enable-pagination-in-listing-chain-query.md diff --git a/.changelog/unreleased/bug-fixes/2408-bump-cosmossdk-math.md b/.changelog/v6.4.0/bug-fixes/2408-bump-cosmossdk-math.md similarity index 100% rename from .changelog/unreleased/bug-fixes/2408-bump-cosmossdk-math.md rename to .changelog/v6.4.0/bug-fixes/2408-bump-cosmossdk-math.md diff --git a/.changelog/unreleased/dependencies/2390-bump-comet.md b/.changelog/v6.4.0/dependencies/2390-bump-comet.md similarity index 100% rename from .changelog/unreleased/dependencies/2390-bump-comet.md rename to .changelog/v6.4.0/dependencies/2390-bump-comet.md diff --git a/.changelog/unreleased/dependencies/2390-bump-ibc.md b/.changelog/v6.4.0/dependencies/2390-bump-ibc.md similarity index 100% rename from .changelog/unreleased/dependencies/2390-bump-ibc.md rename to .changelog/v6.4.0/dependencies/2390-bump-ibc.md diff --git a/.changelog/unreleased/dependencies/2408-bump-cosmossdk-math.md b/.changelog/v6.4.0/dependencies/2408-bump-cosmossdk-math.md similarity index 100% rename from .changelog/unreleased/dependencies/2408-bump-cosmossdk-math.md rename to .changelog/v6.4.0/dependencies/2408-bump-cosmossdk-math.md diff --git a/.changelog/v6.4.0/dependencies/2458-bump-sdk.md b/.changelog/v6.4.0/dependencies/2458-bump-sdk.md new file mode 100644 index 0000000000..3d30e40bb5 --- /dev/null +++ b/.changelog/v6.4.0/dependencies/2458-bump-sdk.md @@ -0,0 +1,3 @@ +- Bump [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) to + [v0.50.11](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.11) + ([\#2458](https://github.com/cosmos/interchain-security/pull/2458)) \ No newline at end of file diff --git a/.changelog/unreleased/features/2101-introduce-priority-validators.md b/.changelog/v6.4.0/features/2101-introduce-priority-validators.md similarity index 100% rename from .changelog/unreleased/features/2101-introduce-priority-validators.md rename to .changelog/v6.4.0/features/2101-introduce-priority-validators.md diff --git a/.changelog/unreleased/features/2366-add-genesis-time-query.md b/.changelog/v6.4.0/features/2366-add-genesis-time-query.md similarity index 100% rename from .changelog/unreleased/features/2366-add-genesis-time-query.md rename to .changelog/v6.4.0/features/2366-add-genesis-time-query.md diff --git a/.changelog/unreleased/features/2372-vscmatured-packets.md b/.changelog/v6.4.0/features/2372-vscmatured-packets.md similarity index 100% rename from .changelog/unreleased/features/2372-vscmatured-packets.md rename to .changelog/v6.4.0/features/2372-vscmatured-packets.md diff --git a/.changelog/unreleased/features/2378-allow-chain-id-updates.md b/.changelog/v6.4.0/features/2378-allow-chain-id-updates.md similarity index 100% rename from .changelog/unreleased/features/2378-allow-chain-id-updates.md rename to .changelog/v6.4.0/features/2378-allow-chain-id-updates.md diff --git a/.changelog/unreleased/features/2399-opt-in-chain-needs-active-validator-to-launch.md b/.changelog/v6.4.0/features/2399-opt-in-chain-needs-active-validator-to-launch.md similarity index 100% rename from .changelog/unreleased/features/2399-opt-in-chain-needs-active-validator-to-launch.md rename to .changelog/v6.4.0/features/2399-opt-in-chain-needs-active-validator-to-launch.md diff --git a/.changelog/v6.4.0/features/2400-preccv.md b/.changelog/v6.4.0/features/2400-preccv.md new file mode 100644 index 0000000000..2354aa18d0 --- /dev/null +++ b/.changelog/v6.4.0/features/2400-preccv.md @@ -0,0 +1,13 @@ +- Enable existing (standalone) chains to use the existing client (and connection) + to the provider chain when becoming a consumer chain. This feature introduces + the following changes. + ([\#2400](https://github.com/cosmos/interchain-security/pull/2400)) + + - Add `connection_id` to `ConsumerInitializationParameters`, the ID of + the connection end _on the provider chain_ on top of which the CCV channel will + be established. Consumer chain owners can set `connection_id` to a valid ID in + order to reuse the underlying clients. + - Add `connection_id` to the consumer genesis state, the ID of the connection + end _on the consumer chain_ on top of which the CCV channel will be established. + If `connection_id` is a valid ID, then the consumer chain will use the underlying + client as the provider client and it will initiate the channel handshake. \ No newline at end of file diff --git a/.changelog/unreleased/features/2403-customizable-slashing-jailing.md b/.changelog/v6.4.0/features/2403-customizable-slashing-jailing.md similarity index 100% rename from .changelog/unreleased/features/2403-customizable-slashing-jailing.md rename to .changelog/v6.4.0/features/2403-customizable-slashing-jailing.md diff --git a/.changelog/unreleased/improvements/2381-remove-governance-whitelisting.md b/.changelog/v6.4.0/improvements/2381-remove-governance-whitelisting.md similarity index 100% rename from .changelog/unreleased/improvements/2381-remove-governance-whitelisting.md rename to .changelog/v6.4.0/improvements/2381-remove-governance-whitelisting.md diff --git a/.changelog/unreleased/state-breaking/2101-introduce-priority-validators.md b/.changelog/v6.4.0/state-breaking/2101-introduce-priority-validators.md similarity index 100% rename from .changelog/unreleased/state-breaking/2101-introduce-priority-validators.md rename to .changelog/v6.4.0/state-breaking/2101-introduce-priority-validators.md diff --git a/.changelog/unreleased/state-breaking/2372-vscmatured-packets.md b/.changelog/v6.4.0/state-breaking/2372-vscmatured-packets.md similarity index 100% rename from .changelog/unreleased/state-breaking/2372-vscmatured-packets.md rename to .changelog/v6.4.0/state-breaking/2372-vscmatured-packets.md diff --git a/.changelog/unreleased/state-breaking/2378-allow-chain-id-updates.md b/.changelog/v6.4.0/state-breaking/2378-allow-chain-id-updates.md similarity index 100% rename from .changelog/unreleased/state-breaking/2378-allow-chain-id-updates.md rename to .changelog/v6.4.0/state-breaking/2378-allow-chain-id-updates.md diff --git a/.changelog/unreleased/state-breaking/2381-remove-governance-whitelisting.md b/.changelog/v6.4.0/state-breaking/2381-remove-governance-whitelisting.md similarity index 100% rename from .changelog/unreleased/state-breaking/2381-remove-governance-whitelisting.md rename to .changelog/v6.4.0/state-breaking/2381-remove-governance-whitelisting.md diff --git a/.changelog/v6.4.0/state-breaking/2400-preccv.md b/.changelog/v6.4.0/state-breaking/2400-preccv.md new file mode 100644 index 0000000000..6225d97caf --- /dev/null +++ b/.changelog/v6.4.0/state-breaking/2400-preccv.md @@ -0,0 +1,3 @@ +- Enable existing (standalone) chains to use the existing client (and connection) + to the provider chain when becoming a consumer chain. + ([\#2400](https://github.com/cosmos/interchain-security/pull/2400)) \ No newline at end of file diff --git a/.changelog/unreleased/state-breaking/2403-customizable-slashing-jailing.md b/.changelog/v6.4.0/state-breaking/2403-customizable-slashing-jailing.md similarity index 100% rename from .changelog/unreleased/state-breaking/2403-customizable-slashing-jailing.md rename to .changelog/v6.4.0/state-breaking/2403-customizable-slashing-jailing.md diff --git a/.changelog/v6.4.0/state-breaking/2458-bump-sdk.md b/.changelog/v6.4.0/state-breaking/2458-bump-sdk.md new file mode 100644 index 0000000000..3d30e40bb5 --- /dev/null +++ b/.changelog/v6.4.0/state-breaking/2458-bump-sdk.md @@ -0,0 +1,3 @@ +- Bump [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) to + [v0.50.11](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.11) + ([\#2458](https://github.com/cosmos/interchain-security/pull/2458)) \ No newline at end of file diff --git a/.changelog/v6.4.0/summary.md b/.changelog/v6.4.0/summary.md new file mode 100644 index 0000000000..9015a49458 --- /dev/null +++ b/.changelog/v6.4.0/summary.md @@ -0,0 +1 @@ +*January 7, 2025* diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 65ba34f57b..6611e95036 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,4 +2,4 @@ # Primary repo maintainers -* @cosmos/informal_interchain-security_maintain +* @cosmos/informal_interchain-security_maintain @cosmos/interchain_inc_ics_write diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 12d4d9d017..a01d35e68c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -87,3 +87,13 @@ updates: open-pull-requests-limit: 0 labels: - dependencies + + - package-ecosystem: gomod + directory: "/" + schedule: + interval: daily + target-branch: "release/v6.4.x" + # Only allow automated security-related dependency updates on release branches. + open-pull-requests-limit: 0 + labels: + - dependencies \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 31cafc8a77..cec60ee8a2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -208,3 +208,29 @@ jobs: if: env.GIT_DIFF run: | make verify-models + + test-interchain: + runs-on: Gaia-Runner-medium + 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: interchain tests + if: env.GIT_DIFF + run: | + make test-interchain diff --git a/.mergify.yml b/.mergify.yml index 038ba5cf54..ebf5f2e989 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -65,4 +65,12 @@ pull_request_rules: actions: backport: branches: - - release/v6.3.x \ No newline at end of file + - release/v6.3.x + - name: Backport patches to the release/v6.4.x branch + conditions: + - base=main + - label=A:backport/v6.4.x + actions: + backport: + branches: + - release/v6.4.x \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 89d0016b66..0fca232635 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,110 @@ # CHANGELOG +## v6.4.0 + +*January 7, 2025* + +### API BREAKING + +- Enable existing (standalone) chains to use the existing client (and connection) + to the provider chain when becoming a consumer chain. This feature introduces + the following API-breaking changes. + ([\#2400](https://github.com/cosmos/interchain-security/pull/2400)) + + - Add `connection_id` and `preCCV` to `ConsumerGenesisState`, the consumer + genesis state created by the provider chain. If the `connection_id` is not empty, + `preCCV` is set to true and both `provider.client_state` and `provider.consensus_state` + are set to nil (as the consumer doesn't need to create a new provider client). + As a result, for older versions of consumers, the `connection_id` in + `ConsumerInitializationParameters` must be empty and the resulting `ConsumerGenesisState` + needs to be adapted, i.e., both `connection_id` and `preCCV` need to be removed. + +### BUG FIXES + +- Bump [cosmossdk.io/math](https://github.com/cosmos/cosmos-sdk/tree/main/math) to + [v1.4.0](https://github.com/cosmos/cosmos-sdk/tree/math/v1.4.0) + ([\#2408](https://github.com/cosmos/gaia/pull/2408)) +- `[x/consumer]` Updated `genesis transform` CLI to transform `consumer-genesis` content exported by v6.2 providers for consumer chains at version v5. Removed transformation for older consumer versions. + ([\#2373](https://github.com/cosmos/interchain-security/pull/2373)) +- `[x/provider]` Fixed pagination bug in query for listing the consumer chains. + ([\#2398](https://github.com/cosmos/interchain-security/pull/2398)) +- `[x/provider]` Fixed pagination in the list consumer chains query. + ([\#2377](https://github.com/cosmos/interchain-security/pull/2377)) + +### DEPENDENCIES + +- Bump [CometBFT](https://github.com/cometbft/cometbft) to + [v0.38.15](https://github.com/cometbft/cometbft/releases/tag/v0.38.15). + ([\#2390](https://github.com/cosmos/interchain-security/pull/2390)) +- Bump [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) to + [v0.50.11](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.11) + ([\#2458](https://github.com/cosmos/interchain-security/pull/2458)) +- Bump [cosmossdk.io/math](https://github.com/cosmos/cosmos-sdk/tree/main/math) to + [v1.4.0](https://github.com/cosmos/cosmos-sdk/tree/math/v1.4.0) + ([\#2408](https://github.com/cosmos/gaia/pull/2408)) +- Bump [ibc-go](https://github.com/cosmos/ibc-go) to + [v8.5.2](https://github.com/cosmos/ibc-go/releases/tag/v8.5.2). + ([\#2390](https://github.com/cosmos/interchain-security/pull/2390)) + +### FEATURES + +- Enable existing (standalone) chains to use the existing client (and connection) + to the provider chain when becoming a consumer chain. This feature introduces + the following changes. + ([\#2400](https://github.com/cosmos/interchain-security/pull/2400)) + + - Add `connection_id` to `ConsumerInitializationParameters`, the ID of + the connection end _on the provider chain_ on top of which the CCV channel will + be established. Consumer chain owners can set `connection_id` to a valid ID in + order to reuse the underlying clients. + - Add `connection_id` to the consumer genesis state, the ID of the connection + end _on the consumer chain_ on top of which the CCV channel will be established. + If `connection_id` is a valid ID, then the consumer chain will use the underlying + client as the provider client and it will initiate the channel handshake. +- `[x/consumer]` Remove `VSCMaturedPackets`. Consumer-side changes for [ADR 018](https://cosmos.github.io/interchain-security/adrs/adr-018-remove-vscmatured#consumer-changes-r2). + ([\#2372](https://github.com/cosmos/interchain-security/pull/2372)) +- `[x/provider]` Add query for consumer genesis time, + which corresponds to creation time of consumer clients. +([\#2366](https://github.com/cosmos/interchain-security/pull/2366)) +- `[x/provider]` Allow consumer chains to specify a list of priority validators that are included in the validator set before other validators are considered + ([\#2101](https://github.com/cosmos/interchain-security/pull/2101)) +- `[x/provider]` Allow the chain id of a consumer chain to be updated before the chain + launches. ([\#2378](https://github.com/cosmos/interchain-security/pull/2378)) +- `[x/provider]` Enable the customization of the slashing and jailing conditions + for infractions committed by validators on consumer chains (as per + [ADR 020](https://cosmos.github.io/interchain-security/adrs/adr-020-cutomizable_slashing_and_jailing)). + Every consumer chain can decide the punishment for every type of infraction. + ([\#2403](https://github.com/cosmos/interchain-security/pull/2403)) +- `[x/provider]` Prevent Opt-In chains from launching, unless at least one active validator has opted-in to them. + ([\#2101](https://github.com/cosmos/interchain-security/pull/2399)) + +### IMPROVEMENTS + +- `[x/democracy/governance]` Removal of consumer governance whitelisting functionality + ([\#2381](https://github.com/cosmos/interchain-security/pull/2381)) + +### STATE BREAKING + +- Allow the chain id of a consumer chain to be updated before the chain + launches. ([\#2378](https://github.com/cosmos/interchain-security/pull/2378)) +- Bump [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) to + [v0.50.11](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.11) + ([\#2458](https://github.com/cosmos/interchain-security/pull/2458)) +- Enable existing (standalone) chains to use the existing client (and connection) + to the provider chain when becoming a consumer chain. + ([\#2400](https://github.com/cosmos/interchain-security/pull/2400)) +- `[x/consumer]` Remove `VSCMaturedPackets`. Consumer-side changes for [ADR 018](https://cosmos.github.io/interchain-security/adrs/adr-018-remove-vscmatured#consumer-changes-r2). + ([\#2372](https://github.com/cosmos/interchain-security/pull/2372)) +- `[x/democracy/governance]` Removal of consumer governance whitelisting functionality + ([\#2381](https://github.com/cosmos/interchain-security/pull/2381)) +- `[x/provider]` Allow consumer chains to specify a list of priority validators that are included in the validator set before other validators are considered + ([\#2101](https://github.com/cosmos/interchain-security/pull/2101)) +- `[x/provider]` Enable the customization of the slashing and jailing conditions + for infractions committed by validators on consumer chains (as per + [ADR 020](https://cosmos.github.io/interchain-security/adrs/adr-020-cutomizable_slashing_and_jailing)). + Every consumer chain can decide the punishment for every type of infraction. + ([\#2403](https://github.com/cosmos/interchain-security/pull/2403)) + ## v6.3.0 *October 18, 2024* diff --git a/FEATURES.md b/FEATURES.md index fd60e03c14..d97c3038d7 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -2,21 +2,23 @@ The following table indicates the major ICS features available in the [currently active releases](./RELEASES.md#version-matrix): -| Feature | `v4.0.0` | `v4.3.1` | `v4.3.1-lsm` | `v4.4.0` | `v5.0.0` | `v5.2.0` | `v6.1.0` | -|---------|---------:|---------:|-------------:|---------:|---------:|---------:|----------:| +| Feature | `v4.0.0` | `v4.4.0` | `v4.5.0` | `v5.0.0` | `v5.2.0` | `v6.1.0` | `v6.3.0` | `v6.4.0` | +|---------|---------:|---------:|---------:|---------:|----------:|---------:|---------:|---------:| | [Channel initialization: new chains](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#channel-initialization-new-chains) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Validator set update](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#validator-set-update) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Completion of unbonding operations](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#completion-of-unbonding-operations) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Consumer initiated slashing](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#consumer-initiated-slashing) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Reward distribution](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#reward-distribution) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Consumer chain removal](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/methods.md#consumer-chain-removal) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Key assignment](https://github.com/cosmos/interchain-security/issues/26) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Jail throttling](https://github.com/cosmos/interchain-security/issues/404) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Soft opt-out](https://github.com/cosmos/interchain-security/issues/851) | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | -| [Channel initialization: existing chains](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#channel-initialization-existing-chains) (aka [Standalone to consumer changeover](https://github.com/cosmos/interchain-security/issues/756)) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Cryptographic verification of equivocation](https://github.com/cosmos/interchain-security/issues/732) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Jail throttling with retries](https://github.com/cosmos/interchain-security/issues/713) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [ICS epochs](https://cosmos.github.io/interchain-security/adrs/adr-014-epochs) | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [Partial Set Security](https://cosmos.github.io/interchain-security/adrs/adr-015-partial-set-security) | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | -| [Inactive Provider Validators](https://cosmos.github.io/interchain-security/adrs/adr-017-allowing-inactive-validators) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | -| [Permissionless](https://cosmos.github.io/interchain-security/adrs/adr-019-permissionless-ics) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | +| [Validator set update](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#validator-set-update) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Completion of unbonding operations](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#completion-of-unbonding-operations) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | +| [Consumer initiated slashing](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#consumer-initiated-slashing) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Reward distribution](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#reward-distribution) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Consumer chain removal](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/methods.md#consumer-chain-removal) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Key assignment](https://github.com/cosmos/interchain-security/issues/26) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Jail throttling](https://github.com/cosmos/interchain-security/issues/404) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Soft opt-out](https://github.com/cosmos/interchain-security/issues/851) | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | +| [Channel initialization: existing chains](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#channel-initialization-existing-chains) (aka [Standalone to consumer changeover](https://github.com/cosmos/interchain-security/issues/756)) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Cryptographic verification of equivocation](https://github.com/cosmos/interchain-security/issues/732) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Jail throttling with retries](https://github.com/cosmos/interchain-security/issues/713) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [ICS epochs](https://cosmos.github.io/interchain-security/adrs/adr-014-epochs) | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Partial Set Security](https://cosmos.github.io/interchain-security/adrs/adr-015-partial-set-security) | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | +| [Inactive Provider Validators](https://cosmos.github.io/interchain-security/adrs/adr-017-allowing-inactive-validators) | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | +| [Permissionless](https://cosmos.github.io/interchain-security/adrs/adr-019-permissionless-ics) | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | +| [ICS Rewards in non-native denoms](https://github.com/cosmos/interchain-security/issues/1634) | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | +| [Customizable Slashing and Jailing](https://cosmos.github.io/interchain-security/adrs/adr-020-cutomizable_slashing_and_jailing) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | diff --git a/Makefile b/Makefile index ab56feb717..9aec35674d 100644 --- a/Makefile +++ b/Makefile @@ -53,11 +53,18 @@ test-integration-cov: go test ./tests/integration/... -timeout 30m -coverpkg=./... -coverprofile=integration-profile.out -covermode=atomic # run interchain tests -# we can use PROVIDER_IMAGE_TAG and PROVIDER_IMAGE_NAME to run tests with a desired docker image, -# including a locally built one that, for example, contains some of our changes that are not yet on the main branch. -# if not provided, default value for PROVIDER_IMAGE_TAG is "latest" and for PROVIDER_IMAGE_NAME "ghcr.io/cosmos/interchain-security" +# we can use PROVIDER_IMAGE_TAG, PROVIDER_IMAGE_NAME, CONSUMER_IMAGE_TAG, CONSUMER_IMAGE_NAME, SOVEREIGN_IMAGE_TAG, and SOVEREIGN_IMAGE_NAME to run +# tests with desired docker images, including locally built ones that, for example, contain some of our changes that are not yet on the main branch. +# if not provided, default value for image tag is "latest" and for image name is "ghcr.io/cosmos/interchain-security" test-interchain: - cd tests/interchain && PROVIDER_IMAGE_NAME=$(PROVIDER_IMAGE_NAME) PROVIDER_IMAGE_TAG=$(PROVIDER_IMAGE_TAG) go test ./... -timeout 30m + cd tests/interchain && \ + PROVIDER_IMAGE_NAME=$(PROVIDER_IMAGE_NAME) \ + PROVIDER_IMAGE_TAG=$(PROVIDER_IMAGE_TAG) \ + SOVEREIGN_IMAGE_NAME=$(SOVEREIGN_IMAGE_NAME) \ + SOVEREIGN_IMAGE_TAG=$(SOVEREIGN_IMAGE_TAG) \ + CONSUMER_IMAGE_NAME=$(CONSUMER_IMAGE_NAME) \ + CONSUMER_IMAGE_TAG=$(CONSUMER_IMAGE_TAG) \ + go test ./... -timeout 30m -v # run mbt tests test-mbt: diff --git a/RELEASES.md b/RELEASES.md index d729e88f51..ab4b19a017 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -7,6 +7,7 @@ - [Stable Release Policy](#stable-release-policy) - [Version Matrix](#version-matrix) - [Backwards Compatibility](#backwards-compatibility) + - [Notes](#notes) ## Semantic Versioning @@ -59,30 +60,28 @@ All missing minor release versions have been discontinued. | Release | End of Life Date | | -------- | ---------------- | | `v4.0.x` | January 24, 2025 | -| `v4.3.x` | January 24, 2025 | | `v4.4.x` | January 24, 2025 | +| `v4.5.x` | January 24, 2025 | | `v5.0.x` | May 9, 2025 | | `v5.2.x` | May 9, 2025 | | `v6.1.x` | Sep 13, 2025 | - -**Note**: Gaia versions on SDK 0.47 (i.e., `v15.0.x` -- `v18.1.x`) use a fork of Cosmos SDK (i.e., `v0.47.x-ics-lsm`) that contains the Liquid Staking Module (LSM). -This means that these versions of Gaia require a fork of ICS. -This fork is maintained by the development team and released using the `-lsm` prefix. -Starting with Gaia `v19.0.x` (that uses SDK 0.50), the fork of ICS is no longer needed. +| `v6.3.x` | Sep 13, 2025 | +| `v6.4.x` | Sep 13, 2025 | ## Version Matrix Versions of Golang, IBC, Cosmos SDK and CometBFT used by ICS in the currently active releases: -| ICS | Golang | IBC | Cosmos SDK | CometBFT | Note | -| ----------------------------------------------------------------------------------- | ------ | ------ | ---------------- | -------- | ----------------------------------- | -| [v4.0.0](https://github.com/cosmos/interchain-security/releases/tag/v4.0.0) | 1.21 | v7.3.1 | v0.47.7 | v0.37.4 | | -| [v4.3.1](https://github.com/cosmos/interchain-security/releases/tag/v4.3.1) | 1.21 | v7.6.0 | v0.47.12 | v0.37.6 | | -| [v4.3.1-lsm](https://github.com/cosmos/interchain-security/releases/tag/v4.3.1-lsm) | 1.21 | v7.6.0 | v0.47.16-ics-lsm | v0.37.6 | Provider only (Cosmos Hub specific) | -| [v4.4.0](https://github.com/cosmos/interchain-security/releases/tag/v4.4.0) | 1.21 | v7.6.0 | v0.47.12 | v0.37.6 | | -| [v5.0.0](https://github.com/cosmos/interchain-security/releases/tag/v5.0.0) | 1.21 | v8.1.0 | v0.50.4 | v0.38.5 | | -| [v5.2.0](https://github.com/cosmos/interchain-security/releases/tag/v5.2.0) | 1.22 | v8.3.2 | v0.50.8 | v0.38.9 | | -| [v6.1.0](https://github.com/cosmos/interchain-security/releases/tag/v6.1.0) | 1.22 | v8.5.0 | v0.50.9 | v0.38.11 | | +| ICS | Golang | IBC | Cosmos SDK | CometBFT | Note | +| --------------------------------------------------------------------------- | ------ | ------ | ---------- | -------- | ---- | +| [v4.0.0](https://github.com/cosmos/interchain-security/releases/tag/v4.0.0) | 1.21 | v7.3.1 | v0.47.7 | v0.37.4 | | +| [v4.4.0](https://github.com/cosmos/interchain-security/releases/tag/v4.4.0) | 1.21 | v7.6.0 | v0.47.12 | v0.37.6 | | +| [v4.5.0](https://github.com/cosmos/interchain-security/releases/tag/v4.4.0) | 1.21 | v7.6.0 | v0.47.12 | v0.37.6 | | +| [v5.0.0](https://github.com/cosmos/interchain-security/releases/tag/v5.0.0) | 1.21 | v8.1.0 | v0.50.4 | v0.38.5 | | +| [v5.2.0](https://github.com/cosmos/interchain-security/releases/tag/v5.2.0) | 1.22 | v8.3.2 | v0.50.8 | v0.38.9 | | +| [v6.1.0](https://github.com/cosmos/interchain-security/releases/tag/v6.1.0) | 1.22 | v8.5.0 | v0.50.9 | v0.38.11 | | +| [v6.3.0](https://github.com/cosmos/interchain-security/releases/tag/v6.3.0) | 1.22 | v8.5.1 | v0.50.9 | v0.38.11 | | +| [v6.4.0](https://github.com/cosmos/interchain-security/releases/tag/v6.4.0) | 1.22 | v8.5.2 | v0.50.11 | v0.38.15 | | **Note:** For a list of major ICS features available in the currently active releases, see [FEATURES.md](./FEATURES.md). @@ -92,10 +91,17 @@ A MAJOR version of ICS will always be backwards compatible with the previous MAJ The following table indicates the compatibility of currently active releases: -| Consumer | Provider | `v4.3.1-lsm` | `v5.2.0` | `v6.1.0` | -| -------- | -------- | ------------ | -------- | -------- | -| `v4.0.0` | | ✅ | ✅ | ✅ | -| `v4.3.1` | | ✅ | ✅ | ✅ | -| `v4.4.0` | | ✅ | ✅ | ✅ | -| `v5.0.0` | | ✅ | ✅ | ✅ | -| `v5.2.0` | | ✅ | ✅ | ✅ | +| Consumer | Provider | `v5.2.0` | `v6.1.0` | `v6.3.0` | `v6.4.0` | +| ------------------- | -------- | -------- | -------- | -------- | -------- | +| `v4.0.0` | | ✅ | ✅ | ✅ (1) | ✅ (1) | +| `v4.4.0` | | ✅ | ✅ | ✅ (1) | ✅ (1) | +| `v4.5.0` | | ✅ | ✅ | ✅ | ✅ (1) | +| `v5.0.0` | | ✅ | ✅ | ✅ (1) | ✅ (1) | +| `v5.2.0` | | ✅ | ✅ | ✅ (1) | ✅ (1) | +| `v6.2.0` / `v6.3.0` | | ✅ | ✅ | ✅ | ✅ (1) | +| `v6.4.0` | | ✅ | ✅ | ✅ | ✅ | + +#### Notes +The following adjustments must be made to the CCV consumer genesis state that is obtained from the provider chain after the spawn time is reached in order for the consumer chain to start without errors. + +- (1) Use `interchain-security-cd genesis transform` to transform the consumer genesis file obtained from the provider. \ No newline at end of file diff --git a/TESTING.md b/TESTING.md index 4a50f6d3de..b27719d8aa 100644 --- a/TESTING.md +++ b/TESTING.md @@ -44,6 +44,13 @@ Notably, as-of-now simulation tests do not include any multi-chain testing, so c 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 related make command listed below. +## Interchain Tests +The interchain tests evaluate the fundamental functionalities of both the provider and provider-consumer chains. This includes testing the transition of a sovereign chain to a consumer chain. These tests leverage the interchain framework, where each chain node operates within its own Docker container.To execute these tests, you can use the default ics image built from the latest code on the main branch. Alternatively, tests can be run with a specific image and version, whether published or locally built. The tests are triggered using the make test-interchain command. For more details, see the [Running Tests](#running-tests) section. +- If you wish to build the docker image from the code on your desired branch, run this command: +```bash + docker build -t test-image:local . +``` + ## Running Tests Tests can be run using `make`: @@ -80,6 +87,13 @@ make sim-full #run simulation tests where providerModule.max_provider_consensus_validators=stakingModule.max_validators=100 make sim-full-no-inactive-vals + +#run interchain tests (running with the latest image ghcr.io/cosmos/interchain-security:latest) +make test-interchain +# run interchain tests with specific image(e.g. test-image:local) +make test-interchain PROVIDER_IMAGE_NAME=test-image PROVIDER_IMAGE_TAG=local SOVEREIGN_IMAGE_NAME=test-image SOVEREIGN_IMAGE_TAG=local +# to run single interchain test, first navigate to /tests/interchain directory and run the command for desired test e.g. +# go test -run ^TestMultiValidatorProviderSuite/TestOptInChainCanOnlyStartIfActiveValidatorOptedIn$ ./... ``` Alternatively you can run tests using `go test`: diff --git a/app/consumer-democracy/app.go b/app/consumer-democracy/app.go index 865ea7711c..ce34dc7170 100644 --- a/app/consumer-democracy/app.go +++ b/app/consumer-democracy/app.go @@ -689,8 +689,6 @@ func New( consumerGenesis := consumertypes.GenesisState{} appCodec.MustUnmarshalJSON(appState[consumertypes.ModuleName], &consumerGenesis) - - consumerGenesis.PreCCV = true app.ConsumerKeeper.InitGenesis(sdkCtx, &consumerGenesis) app.Logger().Info("start to run module migrations...") diff --git a/app/consumer/genesis.go b/app/consumer/genesis.go index e16c2f4b0e..8c5c153797 100644 --- a/app/consumer/genesis.go +++ b/app/consumer/genesis.go @@ -10,12 +10,9 @@ import ( "github.com/spf13/cobra" "golang.org/x/exp/maps" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" - - "github.com/cosmos/interchain-security/v6/x/ccv/types" ) // The genesis state of the blockchain is represented here as a map of raw json @@ -31,13 +28,17 @@ type GenesisState map[string]json.RawMessage type IcsVersion string const ( - v4_x_x IcsVersion = "v4.x" - v5_x_x IcsVersion = "v5.x" + v4_x_x IcsVersion = "= v6.2.x // to a format supported by consumer chains version with either SDK v0.47 and ICS < v4.5.0 or SDK v0.50 and ICS < v6.2.0 // This transformation removes the 'consumer_id' parameter from the 'params' field introduced in ICS v6.2.x -func transformToV5(jsonRaw []byte, ctx client.Context) (json.RawMessage, error) { - srcConGen := types.ConsumerGenesisState{} - err := ctx.Codec.UnmarshalJSON(jsonRaw, &srcConGen) - if err != nil { - return nil, fmt.Errorf("reading consumer genesis data failed: %s", err) - } - +func removeConsumerID(genState map[string]json.RawMessage) (map[string]json.RawMessage, error) { // Remove 'consumer_id' from 'params' - params, err := ctx.Codec.MarshalJSON(&srcConGen.Params) + params, err := removeParameterFromParams(genState["params"], "consumer_id") if err != nil { return nil, err } - params, err = removeParameterFromParams(params, "consumer_id") - if err != nil { - return nil, err - } - - // Marshal GenesisState and patch 'params' value - result, err := ctx.Codec.MarshalJSON(&srcConGen) - if err != nil { - return nil, err - } - genState := map[string]json.RawMessage{} - if err := json.Unmarshal(result, &genState); err != nil { - return nil, fmt.Errorf("unmarshalling 'GenesisState' failed: %v", err) - } genState["params"] = params - result, err = json.Marshal(genState) - if err != nil { - return nil, fmt.Errorf("marshalling transformation result failed: %v", err) + return genState, nil +} + +func removeFieldsFromGenesisState(genState map[string]json.RawMessage, keysToRemove []string) (map[string]json.RawMessage, error) { + for _, key := range keysToRemove { + delete(genState, key) // Remove the key from the map if it exists } - return result, nil + return genState, nil } // transformGenesis transforms ccv consumer genesis data to the specified target version // Returns the transformed data or an error in case the transformation failed or the format is not supported by current implementation -func transformGenesis(ctx client.Context, targetVersion IcsVersion, jsonRaw []byte) (json.RawMessage, error) { - var newConsumerGenesis json.RawMessage = nil +func transformGenesis(targetVersion IcsVersion, jsonRaw []byte) (json.RawMessage, error) { var err error + // Unmarshal genesis state from raw msg + genState := map[string]json.RawMessage{} + if err := json.Unmarshal(jsonRaw, &genState); err != nil { + return nil, fmt.Errorf("unmarshalling 'GenesisState' failed: %v", err) + } switch targetVersion { case v4_x_x, v5_x_x: - newConsumerGenesis, err = transformToV5(jsonRaw, ctx) + genState, err = removeConsumerID(genState) + if err != nil { + break + } + genState, err = removeFieldsFromGenesisState(genState, []string{"connection_id"}) + case v4_5_x, v6_x_x: + genState, err = removeFieldsFromGenesisState(genState, []string{"connection_id"}) default: err = fmt.Errorf("unsupported target version '%s'. Run %s --help", targetVersion, version.AppName) @@ -109,6 +103,13 @@ func transformGenesis(ctx client.Context, targetVersion IcsVersion, jsonRaw []by if err != nil { return nil, fmt.Errorf("transformation failed: %v", err) } + + // Marshal genesis state to raw msg + newConsumerGenesis, err := json.Marshal(genState) + if err != nil { + return nil, fmt.Errorf("marshalling transformation result failed: %v", err) + } + return newConsumerGenesis, err } @@ -125,7 +126,6 @@ func TransformConsumerGenesis(cmd *cobra.Command, args []string) error { return err } - clientCtx := client.GetClientContextFromCmd(cmd) version, err := cmd.Flags().GetString("to") if err != nil { return fmt.Errorf("error getting targetVersion %v", err) @@ -136,7 +136,7 @@ func TransformConsumerGenesis(cmd *cobra.Command, args []string) error { } // try to transform data to target format - newConsumerGenesis, err := transformGenesis(clientCtx, targetVersion, jsonRaw) + newConsumerGenesis, err := transformGenesis(targetVersion, jsonRaw) if err != nil { return err } @@ -165,17 +165,17 @@ func NewDefaultGenesisState(cdc codec.JSONCodec) GenesisState { func GetConsumerGenesisTransformCmd() *cobra.Command { cmd := &cobra.Command{ Use: "transform [-to version] genesis-file", - Short: "Transform CCV consumer genesis data exported to a specific target format", + Short: "Transform consumer module genesis data exported to a specific target format", Long: strings.TrimSpace( fmt.Sprintf(` -Transform the consumer genesis data exported from a provider version v5.x v6.x to a specified consumer target version. +Transform the consumer genesis data exported from a provider version >= v6.3.x to a specified consumer target version. The result is printed to STDOUT. Note: Content to be transformed is not the consumer genesis file itself but the exported content from provider chain which is used to patch the consumer genesis file! Example: $ %s transform /path/to/ccv_consumer_genesis.json -$ %s --to v2.x transform /path/to/ccv_consumer_genesis.json +$ %s --to v5.x transform /path/to/ccv_consumer_genesis.json `, version.AppName, version.AppName), ), Args: cobra.RangeArgs(1, 2), diff --git a/app/consumer/genesis_test.go b/app/consumer/genesis_test.go index 8c9266f626..9e29d8570f 100644 --- a/app/consumer/genesis_test.go +++ b/app/consumer/genesis_test.go @@ -22,15 +22,18 @@ import ( ) const ( - V6x = "v6.x" - V5x = "v5.x" - V4x = "v4.x" + Provider_v6_3_x = "v6.3.x" + Provider_v6_4_x = "v6.4.x" + Consumer_v6_x_x = " expect empty string when unmarshalled to v6 require.EqualValues(t, "", resultField, "Field %s does not match", fieldName) + } else if fieldName == "ConnectionId" && !shouldContainConnectionId(targetVersion) { + // ConnectionId is not present in expect empty string when unmarshalled it + require.EqualValues(t, "", resultField, "Field %s does not match", fieldName) } else { require.EqualValues(t, srcField, resultField, "Field %s does not match", fieldName) } @@ -288,7 +442,7 @@ func CheckGenesisTransform(t *testing.T, sourceVersion string, targetVersion str for i := 0; i < srcParams.NumField(); i++ { fieldName := srcType.Field(i).Name // Skip Params field as it was checked above - if fieldName == "Params" { + if fieldName == "Params" || fieldName == "ConnectionId" { continue } srcField := srcParams.Field(i).Interface() @@ -297,3 +451,24 @@ func CheckGenesisTransform(t *testing.T, sourceVersion string, targetVersion str require.EqualValues(t, srcField, resultField, "Field %s does not match", fieldName) } } + +func shouldContainConsumerId(version string) bool { + switch version { + case Consumer_v6_x_x, Consumer_v4_5_x: + return true + case Consumer_v5_x_x, Consumer_v4_x_x: + return false + } + + return false +} + +func shouldContainConnectionId(version string) bool { + switch version { + case Consumer_v4_x_x, Consumer_v4_5_x, Consumer_v5_x_x, Consumer_v6_x_x: + return false + // future greater versions will contain this fields and should return true + } + + return false +} diff --git a/docs/docs/build/modules/02-provider.md b/docs/docs/build/modules/02-provider.md index e3027d49b4..f9b7a1b65c 100644 --- a/docs/docs/build/modules/02-provider.md +++ b/docs/docs/build/modules/02-provider.md @@ -1560,6 +1560,7 @@ init_params: spawn_time: "2024-09-26T06:55:14.616054Z" transfer_timeout_period: 3600s unbonding_period: 1209600s + connection_id: "" metadata: description: description of your chain and all other relevant information metadata: some metadata about your chain @@ -1715,7 +1716,8 @@ where `update-consumer-msg.json` contains: "consumer_redistribution_fraction": "0.75", "blocks_per_distribution_transmission": "1500", "historical_entries": "1000", - "distribution_transmission_channel": "" + "distribution_transmission_channel": "", + "connection_id": "" }, "power_shaping_parameters":{ "top_N": 0, diff --git a/docs/docs/consumer-development/changeover-procedure.md b/docs/docs/consumer-development/changeover-procedure.md index 17b8faa713..1f9f88d5ae 100644 --- a/docs/docs/consumer-development/changeover-procedure.md +++ b/docs/docs/consumer-development/changeover-procedure.md @@ -4,160 +4,64 @@ sidebar_position: 6 # 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 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. + +In a nutshell, to become an ICS consumer chain, a standalone chain needs to add the `x/ccv/consumer` module (via a coordinated upgrade) and to transfer validation responsibilities to the provider validators as stated in `consumer_genesis.json`. The relevant protocol specifications are available below: * [ICS-28 with existing chains](https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/overview_and_basic_concepts.md#channel-initialization-existing-chains). * [ADR in ICS repo](../adrs/adr-010-standalone-changeover.md) -## Overview +## Consumers on ICS Version `v6.4.0+` -Standalone to consumer changeover procedure can roughly be separated into 4 parts: +For chains that are using ICS `v6.4.0` or newer, the standalone to consumer changeover consists of the following steps. -### 1. `MsgCreateConsumer` submitted to the `provider` chain -The `MsgCreateConsumer` is equivalent to the "normal" `MsgCreateConsumer` message submitted by new consumer chains. +### 1. Create a new consumer chain on the provider -However, here are the most important notes and differences between a new consumer chain and a standalone chain performing a changeover: +Submit a `MsgCreateConsumer` message to the provider chain. This is a "normal" [MsgCreateConsumer message](../build/modules/02-provider.md#msgcreateconsumer) as described in the [onboarding checklist](./onboarding.md), but with the following important notes. -* `chain_id` must be equal to the standalone chain id -* `initial_height` field has additional rules to abide by: +* `chain_id` **MUST** be equal to the standalone chain id. +* The consumer initialization parameters (i.e., `initialization_parameters`) must be adapted for the changeover procedure: + + * `initial_height` is not used as the provider uses an existing client of the standalone chain.. + * `spawn_time` is the time on the provider when the consumer module genesis state is being generated, + which means that at this time the provide creates the initial validator set that will validate the standalone chain once it becomes a consumer chain. + Consequently, `spawn_time` **MUST** occur before the standalone chain is upgraded and the consumer module is added as the upgrade requires the consumer module genesis state. + * `unbonding_period` **MUST** correspond to the value used on the standalone chain. + * `distribution_transmission_channel` **SHOULD** be set to the canonical IBC token transfer channel between the provider and the standalone chain. This will preserve the `ibc denom` that may already be in use. + * `connection_id` **MUST** be set to the ID of the connection end on the provider chain on top of which the canonical IBC token transfer channel was created. + +### 2. Add consumer module to standalone chain -:::caution -``` -{ -... - "initial_height" : { - // must correspond to current revision number of standalone chain - // e.g. stride-1 => "revision_number": 1 - "revision_number": 1, +The standalone chain **MUST** go through an upgrade to include the `x/ccv/consumer` module. +Note that adding the `x/ccv/consumer` module requires the consumer module genesis state which is created by the provider at `spawn_time`. +Consequently, the `spawn_time` **MUST** occur before this upgrade. - // must correspond to a height that is at least 1 block after the upgrade - // that will add the `consumer` module to the standalone chain - // e.g. "upgrade_height": 100 => "revision_height": 101 - "revision_height": 101, - }, -... -} +Note that the consumer module genesis state can be obtained from the provider using the [consumer genesis query](../build/modules/02-provider.md#consumer-genesis), i.e., +```shell +interchain-security-pd query provider consumer-genesis [consumer-id] [flags] ``` -::: - -* `genesis_hash` can be safely ignored because the chain is already running. A hash of the standalone chain's initial genesis may be used - -* `binary_hash` may not be available ahead of time. All chains performing the changeover go through rigorous testing - if bugs are caught and fixed the hash listed in the proposal may not be the most recent one. - -* `spawn_time` listed in the proposal MUST be before the `upgrade_height` listed in the upgrade proposal on the standalone chain. -:::caution -`spawn_time` must occur before the `upgrade_height` on the standalone chain is reached because the `provider` chain must generate the `ConsumerGenesis` that contains the **validator set** that will be used after the changeover. -::: - -* `unbonding_period` must correspond to the value used on the standalone chain. Otherwise, the clients used for the ccv protocol may be incorrectly initialized. - -* `distribution_transmission_channel` **should be set**. - -:::note -Populating `distribution_transmission_channel` will enable the standalone chain to reuse 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. - -If the parameter is not set, a new channel will be created. -::: - -* `ccv_timeout_period` has no important notes - -* `transfer_timeout_period` has no important notes - -* `consumer_redistribution_fraction` has no important notes - -* `blocks_per_distribution_transmission` has no important notes - -* `historical_entries` has no important notes - - -### 2. upgrade proposal on standalone chain - -The standalone chain creates an upgrade proposal to include the `interchain-security/x/ccv/consumer` module. - -:::caution -The upgrade height in the proposal should correspond to a height that is after the `spawn_time` in the `MsgCreateConsumer` submitted to the `provider` chain. -::: - -Otherwise, the upgrade is indistinguishable from a regular on-chain upgrade proposal. - -### 3. spawn time is reached - -When the `spawn_time` is reached on the `provider` it will generate a `ConsumerGenesis` that contains the validator set that will supersede the `standalone` validator set. - -This `ConsumerGenesis` must be available on the standalone chain during the on-chain upgrade. - -### 4. standalone chain upgrade - -Performing the on-chain upgrade on the standalone chain will add the `ccv/consumer` module and allow the chain to become a `consumer` of Interchain Security. - -:::caution -The `ConsumerGenesis` must be exported to a file and placed in the correct folder on the standalone chain before the upgrade. +The consumer genesis state must be exported to a file and placed in the correct folder on the standalone chain before the upgrade. The file must be placed at the exact specified location, otherwise the upgrade will not be executed correctly. +Usually the file is placed in `$NODE_HOME/config`, but the file name and the exact directory is dictated by the upgrade code on the `standalone` chain. -Usually the file is placed in `$NODE_HOME/config`, but the file name and the exact directory is dictated by the upgrade code on the `standalone` chain. -* please check exact instructions provided by the `standalone` chain team -::: - -After the `genesis.json` file has been made available, the process is equivalent to a normal on-chain upgrade. The standalone validator set will sign the next couple of blocks before transferring control to `provider` validator set. - -The standalone validator set can still be slashed for any infractions if evidence is submitted within the `unboding_period`. - -#### Notes - -The changeover procedure may be updated in the future to create a seamless way of providing the validator set information to the standalone chain. - -## Onboarding Checklist - -This onboarding checklist is slightly different from the one under [Onboarding](./onboarding.md) - -Additionally, you can check the [testnet repo](https://github.com/cosmos/testnets/blob/master/interchain-security/CONSUMER_LAUNCH_GUIDE.md) for a comprehensive guide on preparing and launching consumer chains. - -## 1. Complete testing & integration - -- [ ] test integration with gaia -- [ ] test your protocol with supported relayer versions (minimum hermes 1.4.1) -- [ ] test the changeover procedure -- [ ] reach out to the ICS team if you are facing issues +After the `consumer_genesis.json` file has been made available, the process is equivalent to a normal on-chain upgrade. The standalone validator set will sign the next couple of blocks before transferring control to the initial ICS validator set. -## 2. Create an Onboarding Repository +Once upgraded, the `x/ccv/consumer` module will act as the "staking module" for the consumer chain, i.e., it will provide the validator set to the consensus engine. For staking a native token (e.g., for governance), the `x/ccv/democracy/staking` module allows the cosmos-sdk `x/staking` module to be used alongside the `x/ccv/consumer` module. For more details, check out the [democracy modules](../build/modules/04-democracy.md). -To help validators and other node runners onboard onto your chain, please prepare a repository with information on how to run your chain. +## Consumers on ICS Version `< v6.4.0` -This should include (at minimum): +Chains that are using older version of ICS (i.e., `< v6.4.0`), must "force" the provider to create a new client of the standalone chain (on top of which the CCV channel will be created). +This is because older versions of the consumer module expects both a client state and a consensus state in order to create a new provider client. +Therefore, when creating a new consumer chain on the provider, the following changes are necessary in the consumer initialization parameters: -- [ ] genesis.json with CCV data (after spawn time passes). Check if CCV data needs to be transformed (see [Transform Consumer Genesis](./consumer-genesis-transformation.md)) -- [ ] information about relevant seed/peer nodes you are running -- [ ] relayer information (compatible versions) -- [ ] copy of your governance proposal (as JSON) -- [ ] a script showing how to start your chain and connect to peers (optional) -- [ ] take feedback from other developers, validators and community regarding your onboarding repo and make improvements where applicable - -Example of such a repository can be found [here](https://github.com/hyphacoop/ics-testnets/tree/main/game-of-chains-2022/sputnik). - -## 3. Submit a `MsgCreateConsumer` to the provider - -Before you submit a `MsgCreateConsumer` message, please provide a `spawn_time` that is **before** the `upgrade_height` of the upgrade that will introduce the `ccv module` to your chain. -:::danger -If the `spawn_time` happens after your `upgrade_height` the provider will not be able to communicate the new validator set to be used after the changeover. -::: - -Additionally, reach out to the community via the [forum](https://forum.cosmos.network/) to formalize your intention to become an ICS consumer, gather community support and accept feedback from the community, validators and developers. - -- [ ] determine your chain's spawn time -- [ ] determine consumer chain parameters to be put in the proposal -- [ ] take note to include a link to your onboarding repository - -Example of initialization parameters (compare with the [those](./onboarding.md#3-submit-msgcreateconsumer-and-msgupdateconsumer-messages) for chains that *launch* as consumers): - -```js -// ConsumerInitializationParameters provided in MsgCreateConsumer or MsgUpdateConsumer -{ - // Initial height of new consumer chain. +* `connection_id` **MUST** be set to an empty string (i.e., `""`). As a result, the provider will create a new client of the consumer chain and a new connection on top of it. +* `initial_height` will be used by the provider when creating the new consumer client, so it **MUST** be set according to the following rules: + ```json "initial_height" : { // must correspond to current revision number of standalone chain - // e.g. standalone-1 => "revision_number": 1 + // e.g. stride-1 => "revision_number": 1 "revision_number": 1, // must correspond to a height that is at least 1 block after the upgrade @@ -165,65 +69,12 @@ Example of initialization parameters (compare with the [those](./onboarding.md# // e.g. "upgrade_height": 100 => "revision_height": 101 "revision_height": 101, }, - // Hash of the consumer chain genesis state without the consumer CCV module genesis params. - // => not relevant for changeover procedure - "genesis_hash": "d86d756e10118e66e6805e9cc476949da2e750098fcc7634fd0cc77f57a0b2b0", - // Hash of the consumer chain binary that should be run by validators on standalone chain upgrade - // => not relevant for changeover procedure as it may become stale - "binary_hash": "376cdbd3a222a3d5c730c9637454cd4dd925e2f9e2e0d0f3702fc922928583f1", - // 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. - "spawn_time": "2023-02-28T20:40:00.000000Z", - // Unbonding period for the consumer chain. - // It should should be smaller than that of the provider. - "unbonding_period": 1728000000000000, - // Timeout period for CCV related IBC packets. - // Packets are considered timed-out after this interval elapses. - "ccv_timeout_period": 2419200000000000, - // IBC transfer packets will timeout after this interval elapses. - "transfer_timeout_period": 1800000000000, - // 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%. - // The reward amount distributed to the provider is calculated as: 1 - consumer_redistribution_fraction. - "consumer_redistribution_fraction": "0.75", - // BlocksPerDistributionTransmission is the number of blocks between IBC token transfers from the consumer chain to the provider chain. - // eg. send rewards to the provider every 1000 blocks - "blocks_per_distribution_transmission": 1000, - // 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. - "historical_entries": 10000, - // 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 standalone to consumer changeover - // in order to maintain the existing ibc transfer channel - "distribution_transmission_channel": "channel-123" // NOTE: use existing transfer channel if available -} -``` - -:::info -The changeover procedure can be used together with [Partial Set Security](../adrs/adr-015-partial-set-security.md). -This means, that a standalone chain can choose to only be validated by some of the validators of the provider chain by setting `top_N` appropriately, or by -additionally setting a validators-power cap, validator-set cap, etc. by using the [power-shaping parameters](../features/power-shaping.md). -::: - -## 3. Submit an Upgrade Proposal & Prepare for Changeover - -This proposal should add the ccv `consumer` module to your chain. - -- [ ] proposal `upgrade_height` must happen after `spawn_time` in the `MsgCreateConsumer` -- [ ] advise validators about the exact procedure for your chain and point them to your onboarding repository - + ``` -## 4. Upgrade time 🚀 +### Adapt the consumer module genesis state -- [ ] after `spawn_time`, request `ConsumerGenesis` from the `provider` and place it in `/.sovereign/config/genesis.json` -- [ ] upgrade the binary to the one listed in your `UpgradeProposal` +Before the upgrade of the standalone chain (i.e., adding the `x/ccv/consumer` module), the consumer module genesis state created by the provider at `spawn_time` must be adapted to older versions of the consumer module. This consists of two changes. -The chain starts after at least 66.67% of standalone voting power comes online. The consumer chain is considered interchain secured once the "old" validator set signs a couple of blocks and transfers control to the `provider` validator set. +First, by setting `connection_id` in the consumer initialization parameters to an empty string, the provider will set the `preCCV` flag in the `ConsumerGenesisState` struct to `false`. This must be changed to `true` in order to trigger the changeover procedure logic on the `x/ccv/consumer` module. -- [ ] provide a repo with onboarding instructions for validators (it should already be listed in the proposal) -- [ ] genesis.json after `spawn_time` obtained from `provider` (MUST contain the initial validator set) -- [ ] maintenance & emergency contact info (relevant discord, telegram, slack or other communication channels) +Second, the `connection_id` field of `ConsumerGenesisState` must be removed to enable older versions of the consumer module to unmarshal the consumer module genesis state obtained from the provider. This can be done using the `interchain-security-cd genesis transform` CLI command. \ No newline at end of file diff --git a/go.mod b/go.mod index a845992a70..ac2a98be9c 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( cosmossdk.io/math v1.4.0 github.com/cometbft/cometbft v0.38.15 github.com/cometbft/cometbft-db v0.14.1 // indirect - github.com/cosmos/cosmos-sdk v0.50.10 + github.com/cosmos/cosmos-sdk v0.50.11 github.com/cosmos/gogoproto v1.7.0 github.com/cosmos/ics23/go v0.11.0 github.com/golang/mock v1.6.0 @@ -22,13 +22,13 @@ require ( github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.10.0 github.com/tidwall/gjson v1.18.0 - golang.org/x/crypto v0.28.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 golang.org/x/net v0.30.0 // indirect - golang.org/x/sys v0.26.0 // indirect + golang.org/x/sys v0.28.0 // indirect google.golang.org/genproto v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/grpc v1.69.2 - google.golang.org/protobuf v1.35.2 + google.golang.org/protobuf v1.36.1 gopkg.in/yaml.v2 v2.4.0 ) @@ -37,9 +37,9 @@ require ( cloud.google.com/go/compute/metadata v0.5.2 // indirect cloud.google.com/go/iam v1.1.9 // indirect cloud.google.com/go/storage v1.41.0 // indirect - cosmossdk.io/api v0.7.5 + cosmossdk.io/api v0.7.6 cosmossdk.io/core v0.11.1 - cosmossdk.io/depinject v1.0.0 // indirect + cosmossdk.io/depinject v1.1.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect @@ -55,7 +55,7 @@ require ( github.com/cosmos/cosmos-proto v1.0.0-beta.5 github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect - github.com/cosmos/iavl v1.2.0 // indirect + github.com/cosmos/iavl v1.2.2 // indirect github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc @@ -134,8 +134,8 @@ require ( go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 // indirect go.opencensus.io v0.24.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/term v0.25.0 // indirect - golang.org/x/text v0.19.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect google.golang.org/api v0.186.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect @@ -151,7 +151,7 @@ require ( cosmossdk.io/tools/confix v0.1.2 cosmossdk.io/x/evidence v0.1.1 cosmossdk.io/x/feegrant v0.1.1 - cosmossdk.io/x/tx v0.13.5 + cosmossdk.io/x/tx v0.13.7 cosmossdk.io/x/upgrade v0.1.4 github.com/cosmos/cosmos-db v1.1.1 github.com/cosmos/ibc-go/modules/capability v1.0.1 @@ -183,7 +183,7 @@ require ( github.com/creachadair/atomicfile v0.3.1 // indirect github.com/creachadair/tomledit v0.0.24 // indirect github.com/dgraph-io/badger/v4 v4.2.0 // indirect - github.com/emicklei/dot v1.6.1 // indirect + github.com/emicklei/dot v1.6.2 // indirect github.com/fatih/color v1.15.0 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect github.com/go-logr/logr v1.4.2 // indirect @@ -217,7 +217,7 @@ require ( go.uber.org/mock v0.2.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect - golang.org/x/sync v0.8.0 // indirect + golang.org/x/sync v0.10.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect gotest.tools/v3 v3.5.1 // indirect diff --git a/go.sum b/go.sum index 0d0e84e11e..2b1bc465ba 100644 --- a/go.sum +++ b/go.sum @@ -186,16 +186,16 @@ cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xX cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= -cosmossdk.io/api v0.7.5 h1:eMPTReoNmGUm8DeiQL9DyM8sYDjEhWzL1+nLbI9DqtQ= -cosmossdk.io/api v0.7.5/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= +cosmossdk.io/api v0.7.6 h1:PC20PcXy1xYKH2KU4RMurVoFjjKkCgYRbVAD4PdqUuY= +cosmossdk.io/api v0.7.6/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= cosmossdk.io/client/v2 v2.0.0-beta.7 h1:O0PfZL5kC3Sp54wZASLNihQ612Gd6duMp11aM9wawNg= cosmossdk.io/client/v2 v2.0.0-beta.7/go.mod h1:TzwwrzeK+AfSVSESVEIOYO/9xuCh1fPv0HgeocmfVnM= cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= cosmossdk.io/core v0.11.1 h1:h9WfBey7NAiFfIcUhDVNS503I2P2HdZLebJlUIs8LPA= cosmossdk.io/core v0.11.1/go.mod h1:OJzxcdC+RPrgGF8NJZR2uoQr56tc7gfBKhiKeDO7hH0= -cosmossdk.io/depinject v1.0.0 h1:dQaTu6+O6askNXO06+jyeUAnF2/ssKwrrszP9t5q050= -cosmossdk.io/depinject v1.0.0/go.mod h1:zxK/h3HgHoA/eJVtiSsoaRaRA2D5U4cJ5thIG4ssbB8= +cosmossdk.io/depinject v1.1.0 h1:wLan7LG35VM7Yo6ov0jId3RHWCGRhe8E8bsuARorl5E= +cosmossdk.io/depinject v1.1.0/go.mod h1:kkI5H9jCGHeKeYWXTqYdruogYrEeWvBQCw1Pj4/eCFI= 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.5.0 h1:dVdzPJW9kMrnAYyMf1duqacoidB9uZIl+7c6z0mnq0g= @@ -212,8 +212,8 @@ cosmossdk.io/x/evidence v0.1.1 h1:Ks+BLTa3uftFpElLTDp9L76t2b58htjVbSZ86aoK/E4= cosmossdk.io/x/evidence v0.1.1/go.mod h1:OoDsWlbtuyqS70LY51aX8FBTvguQqvFrt78qL7UzeNc= cosmossdk.io/x/feegrant v0.1.1 h1:EKFWOeo/pup0yF0svDisWWKAA9Zags6Zd0P3nRvVvw8= cosmossdk.io/x/feegrant v0.1.1/go.mod h1:2GjVVxX6G2fta8LWj7pC/ytHjryA6MHAJroBWHFNiEQ= -cosmossdk.io/x/tx v0.13.5 h1:FdnU+MdmFWn1pTsbfU0OCf2u6mJ8cqc1H4OMG418MLw= -cosmossdk.io/x/tx v0.13.5/go.mod h1:V6DImnwJMTq5qFjeGWpXNiT/fjgE4HtmclRmTqRVM3w= +cosmossdk.io/x/tx v0.13.7 h1:8WSk6B/OHJLYjiZeMKhq7DK7lHDMyK0UfDbBMxVmeOI= +cosmossdk.io/x/tx v0.13.7/go.mod h1:V6DImnwJMTq5qFjeGWpXNiT/fjgE4HtmclRmTqRVM3w= cosmossdk.io/x/upgrade v0.1.4 h1:/BWJim24QHoXde8Bc64/2BSEB6W4eTydq0X/2f8+g38= cosmossdk.io/x/upgrade v0.1.4/go.mod h1:9v0Aj+fs97O+Ztw+tG3/tp5JSlrmT7IcFhAebQHmOPo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -357,8 +357,8 @@ github.com/cosmos/cosmos-db v1.1.1 h1:FezFSU37AlBC8S98NlSagL76oqBRWq/prTPvFcEJNC github.com/cosmos/cosmos-db v1.1.1/go.mod h1:AghjcIPqdhSLP/2Z0yha5xPH3nLnskz81pBx3tcVSAw= github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRApmI9hjHH05nefC0Ojec= -github.com/cosmos/cosmos-sdk v0.50.10 h1:zXfeu/z653tWZARr/jESzAEiCUYjgJwwG4ytnYWMoDM= -github.com/cosmos/cosmos-sdk v0.50.10/go.mod h1:6Eesrx3ZE7vxBZWpK++30H+Uc7Q4ahQWCL7JKU/LEdU= +github.com/cosmos/cosmos-sdk v0.50.11 h1:LxR1aAc8kixdrs3itO+3a44sFoc+vjxVAOyPFx22yjk= +github.com/cosmos/cosmos-sdk v0.50.11/go.mod h1:gt14Meok2IDCjbDtjwkbUcgVNEpUBDN/4hg9cCUtLgw= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= @@ -366,8 +366,8 @@ github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= 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.2.0 h1:kVxTmjTh4k0Dh1VNL046v6BXqKziqMDzxo93oh3kOfM= -github.com/cosmos/iavl v1.2.0/go.mod h1:HidWWLVAtODJqFD6Hbne2Y0q3SdxByJepHUOeoH4LiI= +github.com/cosmos/iavl v1.2.2 h1:qHhKW3I70w+04g5KdsdVSHRbFLgt3yY3qTMd4Xa4rC8= +github.com/cosmos/iavl v1.2.2/go.mod h1:GiM43q0pB+uG53mLxLDzimxM9l/5N9UuSY3/D0huuVw= github.com/cosmos/ibc-go/modules/capability v1.0.1 h1:ibwhrpJ3SftEEZRxCRkH0fQZ9svjthrX2+oXdZvzgGI= github.com/cosmos/ibc-go/modules/capability v1.0.1/go.mod h1:rquyOV262nGJplkumH+/LeYs04P3eV8oB7ZM4Ygqk4E= github.com/cosmos/ibc-go/v8 v8.5.2 h1:27s9oeD2AxLQF3e9BQsYt9doONyZ7FwZi/qkBv6Sdks= @@ -418,8 +418,8 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/emicklei/dot v1.6.1 h1:ujpDlBkkwgWUY+qPId5IwapRW/xEoligRSYjioR6DFI= -github.com/emicklei/dot v1.6.1/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -1104,8 +1104,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1251,8 +1251,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1349,13 +1349,13 @@ 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.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1366,8 +1366,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1679,8 +1679,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/proto/interchain_security/ccv/consumer/v1/genesis.proto b/proto/interchain_security/ccv/consumer/v1/genesis.proto index 499bb44b3a..8f0a96ea11 100644 --- a/proto/interchain_security/ccv/consumer/v1/genesis.proto +++ b/proto/interchain_security/ccv/consumer/v1/genesis.proto @@ -53,10 +53,15 @@ message GenesisState { // LastTransmissionBlockHeight nil on new chain, filled in on restart. LastTransmissionBlockHeight last_transmission_block_height = 12 [ (gogoproto.nullable) = false ]; - // flag indicating whether the consumer CCV module starts in pre-CCV state + // Flag indicating whether the consumer CCV module starts in pre-CCV state bool preCCV = 13; interchain_security.ccv.v1.ProviderInfo provider = 14 [ (gogoproto.nullable) = false ]; + // The ID of the connection end on the consumer chain on top of which the + // CCV channel will be established. If connection_id == "", a new client of + // the provider chain and a new connection on top of this client are created. + // The new client is initialized using provider.client_state and provider.consensus_state. + string connection_id = 15; } // HeightValsetUpdateID represents a mapping internal to the consumer CCV module diff --git a/proto/interchain_security/ccv/provider/v1/provider.proto b/proto/interchain_security/ccv/provider/v1/provider.proto index 76b283ef6c..cd3dd02b2f 100644 --- a/proto/interchain_security/ccv/provider/v1/provider.proto +++ b/proto/interchain_security/ccv/provider/v1/provider.proto @@ -94,7 +94,7 @@ message ConsumerAdditionProposal { // 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 + // chain. It is most relevant for chains performing a standalone to consumer // changeover in order to maintain the existing ibc transfer channel string distribution_transmission_channel = 14; // Corresponds to the percentage of validators that have to validate the chain under the Top N case. @@ -466,9 +466,15 @@ message ConsumerInitializationParameters { // 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 + // chain. It is most relevant for chains performing a standalone to consumer // changeover in order to maintain the existing ibc transfer channel string distribution_transmission_channel = 11; + // The ID of the connection end on the provider chain on top of which the CCV + // channel will be established. If connection_id == "", a new client of the + // consumer chain and a new connection on top of this client are created. + // Note that a standalone chain can transition to a consumer chain while + // maintaining existing IBC channels to other chains by providing a valid connection_id. + string connection_id = 12; } // PowerShapingParameters contains parameters that shape the validator set that we send to the consumer chain diff --git a/proto/interchain_security/ccv/v1/shared_consumer.proto b/proto/interchain_security/ccv/v1/shared_consumer.proto index d8dad14df7..2a25a46858 100644 --- a/proto/interchain_security/ccv/v1/shared_consumer.proto +++ b/proto/interchain_security/ccv/v1/shared_consumer.proto @@ -87,16 +87,27 @@ message ConsumerParams { message ConsumerGenesisState { ConsumerParams params = 1 [ (gogoproto.nullable) = false ]; ProviderInfo provider = 2 [ (gogoproto.nullable) = false ]; - // true for new chain, false for chain restart. - bool new_chain = 3; // TODO:Check if this is really needed + // True for new chain, false for chain restart. + // This is needed and always set to true; otherwise, new_chain in the consumer + // genesis state will default to false + bool new_chain = 3; + // Flag indicating whether the consumer CCV module starts in pre-CCV state + bool preCCV = 4; + // The ID of the connection end on the consumer chain on top of which the + // CCV channel will be established. If connection_id == "", a new client of + // the provider chain and a new connection on top of this client are created. + // The new client is initialized using client_state and consensus_state. + string connection_id = 5; } // ProviderInfo defines all information a consumer needs from a provider // Shared data type between provider and consumer message ProviderInfo { - // ProviderClientState filled in on new chain, nil on restart. + // The client state for the provider client filled in on new chain, nil on restart. + // If connection_id != "", then client_state is ignored. ibc.lightclients.tendermint.v1.ClientState client_state = 1; - // ProviderConsensusState filled in on new chain, nil on restart. + // The consensus state for the provider client filled in on new chain, nil on restart. + // If connection_id != "", then consensus_state is ignored. ibc.lightclients.tendermint.v1.ConsensusState consensus_state = 2; // InitialValset filled in on new chain and on restart. repeated .tendermint.abci.ValidatorUpdate initial_val_set = 3 diff --git a/tests/e2e/action_rapid_test.go b/tests/e2e/action_rapid_test.go index dffa0286a8..64bd78a5a7 100644 --- a/tests/e2e/action_rapid_test.go +++ b/tests/e2e/action_rapid_test.go @@ -56,10 +56,6 @@ func MarshalAndUnmarshalAction(action interface{}) error { // include generators for all actions that are mentioned in main.go/runStep. func GetActionGen() *rapid.Generator[any] { return rapid.OneOf( - GetStartSovereignChainActionGen().AsAny(), - GetSubmitLegacyUpgradeProposalActionGen().AsAny(), - GetWaitUntilBlockActionGen().AsAny(), - GetChangeoverChainActionGen().AsAny(), GetSendTokensActionGen().AsAny(), GetStartChainActionGen().AsAny(), GetSubmitTextProposalActionGen().AsAny(), @@ -154,47 +150,6 @@ func GetCreateIbcClientsActionGen() *rapid.Generator[CreateIbcClientsAction] { }) } -func GetStartSovereignChainActionGen() *rapid.Generator[StartSovereignChainAction] { - return rapid.Custom(func(t *rapid.T) StartSovereignChainAction { - return StartSovereignChainAction{ - Chain: GetChainIDGen().Draw(t, "Chain"), - Validators: GetStartChainValidatorsGen().Draw(t, "Validators"), - GenesisChanges: rapid.String().Draw(t, "GenesisChanges"), - } - }) -} - -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"), - UpgradeHeight: rapid.Uint64().Draw(t, "UpgradeHeight"), - } - }) -} - -func GetWaitUntilBlockActionGen() *rapid.Generator[WaitUntilBlockAction] { - return rapid.Custom(func(t *rapid.T) WaitUntilBlockAction { - return WaitUntilBlockAction{ - Chain: GetChainIDGen().Draw(t, "Chain"), - Block: rapid.Uint().Draw(t, "Block"), - } - }) -} - -func GetChangeoverChainActionGen() *rapid.Generator[ChangeoverChainAction] { - return rapid.Custom(func(t *rapid.T) ChangeoverChainAction { - return ChangeoverChainAction{ - SovereignChain: GetChainIDGen().Draw(t, "SovereignChain"), - ProviderChain: GetChainIDGen().Draw(t, "ProviderChain"), - Validators: GetStartChainValidatorsGen().Draw(t, "Validators"), - GenesisChanges: rapid.String().Draw(t, "GenesisChanges"), - } - }) -} - func GetSendTokensActionGen() *rapid.Generator[SendTokensAction] { return rapid.Custom(func(t *rapid.T) SendTokensAction { return SendTokensAction{ diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 588a1c5d56..7aefcf7460 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -38,12 +38,10 @@ type ( StartChainAction = e2e.StartChainAction StartChainValidator = e2e.StartChainValidator StartConsumerChainAction = e2e.StartConsumerChainAction - StartSovereignChainAction = e2e.StartSovereignChainAction SubmitConsumerAdditionProposalAction = e2e.SubmitConsumerAdditionProposalAction SubmitConsumerRemovalProposalAction = e2e.SubmitConsumerRemovalProposalAction DelegateTokensAction = e2e.DelegateTokensAction UnbondTokensAction = e2e.UnbondTokensAction - ChangeoverChainAction = e2e.ChangeoverChainAction ) type SendTokensAction struct { @@ -1100,111 +1098,6 @@ func (tr *Chain) transformConsumerGenesis(targetVersion string, genesis []byte) return result } -func (tr Chain) changeoverChain( - action e2e.ChangeoverChainAction, - verbose bool, -) { - consumerGenesis := ".app_state.ccvconsumer = " + tr.getConsumerGenesis(action.ProviderChain, action.SovereignChain) - - consumerGenesisChanges := tr.testConfig.ChainConfigs[action.SovereignChain].GenesisChanges - if consumerGenesisChanges != "" { - consumerGenesis = consumerGenesis + " | " + consumerGenesisChanges - } - if action.GenesisChanges != "" { - consumerGenesis = consumerGenesis + " | " + action.GenesisChanges - } - - tr.startChangeover(e2e.ChangeoverChainAction{ - Validators: action.Validators, - GenesisChanges: consumerGenesis, - }, verbose) -} - -func (tr Chain) startChangeover( - action e2e.ChangeoverChainAction, - verbose bool, -) { - chainConfig := tr.testConfig.ChainConfigs[ChainID("sover")] - type jsonValAttrs struct { - Mnemonic string `json:"mnemonic"` - Allocation string `json:"allocation"` - Stake string `json:"stake"` - ValId string `json:"val_id"` - PrivValidatorKey string `json:"priv_validator_key"` - NodeKey string `json:"node_key"` - IpSuffix string `json:"ip_suffix"` - - ConsumerMnemonic string `json:"consumer_mnemonic"` - ConsumerPrivValidatorKey string `json:"consumer_priv_validator_key"` - StartWithConsumerKey bool `json:"start_with_consumer_key"` - } - - var validators []jsonValAttrs - for _, val := range action.Validators { - validators = append(validators, jsonValAttrs{ - Mnemonic: tr.testConfig.ValidatorConfigs[val.Id].Mnemonic, - NodeKey: tr.testConfig.ValidatorConfigs[val.Id].NodeKey, - ValId: fmt.Sprint(val.Id), - PrivValidatorKey: tr.testConfig.ValidatorConfigs[val.Id].PrivValidatorKey, - Allocation: fmt.Sprint(val.Allocation) + "stake", - Stake: fmt.Sprint(val.Stake) + "stake", - IpSuffix: tr.testConfig.ValidatorConfigs[val.Id].IpSuffix, - - ConsumerMnemonic: tr.testConfig.ValidatorConfigs[val.Id].ConsumerMnemonic, - ConsumerPrivValidatorKey: tr.testConfig.ValidatorConfigs[val.Id].ConsumerPrivValidatorKey, - // if true node will be started with consumer key for each consumer chain - StartWithConsumerKey: tr.testConfig.ValidatorConfigs[val.Id].UseConsumerKey, - }) - } - - vals, err := json.Marshal(validators) - if err != nil { - log.Fatal(err) - } - - // Concat genesis changes defined in chain config, with any custom genesis changes for this chain instantiation - var genesisChanges string - if action.GenesisChanges != "" { - genesisChanges = chainConfig.GenesisChanges + " | " + action.GenesisChanges - } else { - genesisChanges = chainConfig.GenesisChanges - } - - isConsumer := true - changeoverScript := tr.target.GetTestScriptPath(isConsumer, "start-changeover.sh") - cmd := tr.target.ExecCommand( - "/bin/bash", - changeoverScript, chainConfig.UpgradeBinary, string(vals), - "sover", chainConfig.IpPrefix, genesisChanges, - tr.testConfig.TendermintConfigOverride, - ) - - cmdReader, err := cmd.StdoutPipe() - if err != nil { - log.Fatal(err) - } - cmd.Stderr = cmd.Stdout - - if err := cmd.Start(); err != nil { - log.Fatal(err) - } - - scanner := bufio.NewScanner(cmdReader) - - for scanner.Scan() { - out := scanner.Text() - if verbose { - fmt.Println("startChangeover: " + out) - } - if out == done { - break - } - } - if err := scanner.Err(); err != nil { - log.Fatal("startChangeover died", err) - } -} - type AddChainToRelayerAction struct { Chain ChainID Validator ValidatorID diff --git a/tests/e2e/actions_sovereign_chain.go b/tests/e2e/actions_sovereign_chain.go deleted file mode 100644 index a9d9f861e0..0000000000 --- a/tests/e2e/actions_sovereign_chain.go +++ /dev/null @@ -1,198 +0,0 @@ -package main - -import ( - "bufio" - "encoding/json" - "fmt" - "log" - "time" - - e2e "github.com/cosmos/interchain-security/v6/tests/e2e/testlib" -) - -// calls a simplified startup script (start-sovereign.sh) and runs a validator node -// upgrades are simpler with a single validator node since only one node needs to be upgraded -func (tr Chain) startSovereignChain( - action e2e.StartSovereignChainAction, - verbose bool, -) { - chainConfig := tr.testConfig.ChainConfigs["sover"] - type jsonValAttrs struct { - Mnemonic string `json:"mnemonic"` - Allocation string `json:"allocation"` - Stake string `json:"stake"` - ValId string `json:"val_id"` - PrivValidatorKey string `json:"priv_validator_key"` - NodeKey string `json:"node_key"` - IpSuffix string `json:"ip_suffix"` - - ConsumerMnemonic string `json:"consumer_mnemonic"` - ConsumerPrivValidatorKey string `json:"consumer_priv_validator_key"` - StartWithConsumerKey bool `json:"start_with_consumer_key"` - } - - var validators []jsonValAttrs - for _, val := range action.Validators { - validators = append(validators, jsonValAttrs{ - Mnemonic: tr.testConfig.ValidatorConfigs[val.Id].Mnemonic, - NodeKey: tr.testConfig.ValidatorConfigs[val.Id].NodeKey, - ValId: fmt.Sprint(val.Id), - PrivValidatorKey: tr.testConfig.ValidatorConfigs[val.Id].PrivValidatorKey, - Allocation: fmt.Sprint(val.Allocation) + "stake", - Stake: fmt.Sprint(val.Stake) + "stake", - IpSuffix: tr.testConfig.ValidatorConfigs[val.Id].IpSuffix, - - ConsumerMnemonic: tr.testConfig.ValidatorConfigs[val.Id].ConsumerMnemonic, - ConsumerPrivValidatorKey: tr.testConfig.ValidatorConfigs[val.Id].ConsumerPrivValidatorKey, - // if true node will be started with consumer key for each consumer chain - StartWithConsumerKey: tr.testConfig.ValidatorConfigs[val.Id].UseConsumerKey, - }) - } - - vals, err := json.Marshal(validators) - if err != nil { - log.Fatal(err) - } - - // Concat genesis changes defined in chain config, with any custom genesis changes for this chain instantiation - var genesisChanges string - if action.GenesisChanges != "" { - genesisChanges = chainConfig.GenesisChanges + " | " + action.GenesisChanges - } else { - genesisChanges = chainConfig.GenesisChanges - } - - isConsumer := chainConfig.BinaryName != "interchain-security-pd" - testScriptPath := tr.target.GetTestScriptPath(isConsumer, "start-sovereign.sh") - cmd := tr.target.ExecCommand("/bin/bash", testScriptPath, chainConfig.BinaryName, string(vals), - string(chainConfig.ChainId), chainConfig.IpPrefix, genesisChanges, - tr.testConfig.TendermintConfigOverride) - - cmdReader, err := cmd.StdoutPipe() - if err != nil { - log.Fatal(err) - } - cmd.Stderr = cmd.Stdout - - if err := cmd.Start(); err != nil { - log.Fatal(err) - } - - scanner := bufio.NewScanner(cmdReader) - - for scanner.Scan() { - out := scanner.Text() - if verbose { - fmt.Println("startSovereignChain: " + out) - } - if out == done { - break - } - } - if err := scanner.Err(); err != nil { - log.Fatal(err) - } - tr.addChainToRelayer(AddChainToRelayerAction{ - Chain: action.Chain, - Validator: action.Validators[0].Id, - }, verbose) -} - -type UpgradeProposalAction struct { - ChainID ChainID - UpgradeTitle string - Proposer ValidatorID - UpgradeHeight uint64 - Expedited bool -} - -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("Submit proposal:", cmd.String()) - } - - 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) -} - -type WaitUntilBlockAction struct { - Block uint - Chain ChainID -} - -func (tr *Chain) waitUntilBlockOnChain(action WaitUntilBlockAction) { - fmt.Println("waitUntilBlockOnChain is waiting for block:", action.Block) - tr.waitUntilBlock(action.Chain, action.Block, 120*time.Second) - fmt.Println("waitUntilBlockOnChain done waiting for block:", action.Block) -} diff --git a/tests/e2e/json_utils.go b/tests/e2e/json_utils.go index be82e9f45a..c25944a866 100644 --- a/tests/e2e/json_utils.go +++ b/tests/e2e/json_utils.go @@ -216,30 +216,6 @@ func UnmarshalMapToActionType(rawAction json.RawMessage, actionTypeString string if err == nil { return a, nil } - case "main.LegacyUpgradeProposalAction": - var a UpgradeProposalAction - err := json.Unmarshal(rawAction, &a) - if err == nil { - return a, nil - } - case "main.WaitUntilBlockAction": - var a WaitUntilBlockAction - err := json.Unmarshal(rawAction, &a) - if err == nil { - return a, nil - } - case "main.ChangeoverChainAction": - var a ChangeoverChainAction - err := json.Unmarshal(rawAction, &a) - if err == nil { - return a, nil - } - case "main.StartSovereignChainAction": - var a StartSovereignChainAction - err := json.Unmarshal(rawAction, &a) - if err == nil { - return a, nil - } case "main.LightClientEquivocationAttackAction": var a LightClientEquivocationAttackAction err := json.Unmarshal(rawAction, &a) diff --git a/tests/e2e/main.go b/tests/e2e/main.go index bc060ae905..0f935e0ee1 100644 --- a/tests/e2e/main.go +++ b/tests/e2e/main.go @@ -103,12 +103,6 @@ var stepChoices = map[string]StepChoice{ description: "happy path tests", testConfig: DefaultTestCfg, }, - "changeover": { - name: "changeover", - steps: changeoverSteps, - description: "changeover tests", - testConfig: ChangeoverTestCfg, - }, "democracy-reward": { name: "democracy-reward", steps: democracyRegisteredDenomSteps, diff --git a/tests/e2e/steps.go b/tests/e2e/steps.go index b9b635452f..a776d41bd8 100644 --- a/tests/e2e/steps.go +++ b/tests/e2e/steps.go @@ -107,21 +107,6 @@ var multipleConsumers = concatSteps( stepsMultiConsumerDoubleSign("consu", "densu"), // double sign on one of the chains ) -var changeoverSteps = concatSteps( - // start sovereign chain and test delegation operation - - stepRunSovereignChain(), - stepStartProviderChain(), - stepsSovereignTransferChan(), - - // the chain will halt once upgrade height is reached - // after upgrade height is reached, the chain will become a consumer - stepsUpgradeChain(), - stepsChangeoverToConsumer("sover"), - - stepsPostChangeoverDelegate("sover"), -) - var consumerMisbehaviourSteps = concatSteps( // start provider and consumer chain stepsStartChainsForConsumerMisbehaviour("consu"), diff --git a/tests/e2e/steps_sovereign_changeover.go b/tests/e2e/steps_sovereign_changeover.go deleted file mode 100644 index 23d595b8d4..0000000000 --- a/tests/e2e/steps_sovereign_changeover.go +++ /dev/null @@ -1,371 +0,0 @@ -package main - -import ( - gov "github.com/cosmos/cosmos-sdk/x/gov/types/v1" - clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" -) - -// this creates new clients on both chains and a connection (connection-0) between them -// connection-0 is used to create a transfer channel between the chains -// the transfer channel is maintained during the changeover process, meaning that -// the consumer chain will be able to send rewards to the provider chain using the old channel -// as opposed to creating a new transfer channel which happens for new consumers -func stepsSovereignTransferChan() []Step { - return []Step{ - { - Action: CreateIbcClientsAction{ - ChainA: ChainID("sover"), - ChainB: ChainID("provi"), - }, - State: State{}, - }, - { - // this will create channel-0 connection end on both chain - Action: AddIbcChannelAction{ - ChainA: ChainID("sover"), - ChainB: ChainID("provi"), - ConnectionA: 0, - PortA: "transfer", - PortB: "transfer", - Order: "unordered", - Version: "ics20-1", - }, - State: State{}, - }, - } -} - -// steps to convert sovereign to consumer chain -func stepsChangeoverToConsumer(consumerName string) []Step { - s := []Step{ - { - Action: SubmitConsumerAdditionProposalAction{ - PreCCV: true, - Chain: ChainID("provi"), - From: ValidatorID("alice"), - Deposit: 10000001, - ConsumerChain: ChainID(consumerName), - // chain-0 is the transfer channelID that gets created in stepsSovereignTransferChan - // the consumer chain will use this channel to send rewards to the provider chain - // there is no need to create a new channel for rewards distribution - DistributionChannel: "channel-0", - SpawnTime: 0, - InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 111}, // 1 block after upgrade !important - TopN: 100, - }, - State: State{ - ChainID("provi"): ChainState{ - ValBalances: &map[ValidatorID]uint{ - ValidatorID("alice"): 9489999999, - ValidatorID("bob"): 9500000000, - }, - Proposals: &map[uint]Proposal{ - 1: ConsumerAdditionProposal{ - Deposit: 10000001, - Chain: ChainID(consumerName), - SpawnTime: 0, - InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 111}, - Status: gov.ProposalStatus_PROPOSAL_STATUS_VOTING_PERIOD.String(), - }, - }, - }, - }, - }, - { - Action: VoteGovProposalAction{ - Chain: ChainID("provi"), - From: []ValidatorID{ValidatorID("alice"), ValidatorID("bob"), ValidatorID("carol")}, - Vote: []string{"yes", "yes", "yes"}, - PropNumber: 1, - }, - State: State{ - ChainID("provi"): ChainState{ - Proposals: &map[uint]Proposal{ - 1: ConsumerAdditionProposal{ - Deposit: 10000001, - Chain: ChainID(consumerName), - SpawnTime: 0, - InitialHeight: clienttypes.Height{RevisionNumber: 0, RevisionHeight: 111}, - Status: gov.ProposalStatus_PROPOSAL_STATUS_PASSED.String(), - }, - }, - ValBalances: &map[ValidatorID]uint{ - ValidatorID("alice"): 9500000000, - ValidatorID("bob"): 9500000000, - }, - }, - }, - }, - { - Action: ChangeoverChainAction{ - SovereignChain: ChainID(consumerName), - ProviderChain: ChainID("provi"), - Validators: []StartChainValidator{ - {Id: ValidatorID("alice"), Stake: 500000000, Allocation: 10000000000}, - {Id: ValidatorID("bob"), Stake: 500000000, Allocation: 10000000000}, - {Id: ValidatorID("carol"), Stake: 500000000, Allocation: 10000000000}, - }, - }, - State: State{ - ChainID("provi"): ChainState{ - ValPowers: &map[ValidatorID]uint{ - ValidatorID("alice"): 500, - ValidatorID("bob"): 500, - ValidatorID("carol"): 500, - }, - }, - ChainID(consumerName): ChainState{ - ValPowers: &map[ValidatorID]uint{ - // uses val powers from consumer - ValidatorID("alice"): 500, - ValidatorID("bob"): 500, - ValidatorID("carol"): 500, - }, - }, - }, - }, - { - Action: AddIbcConnectionAction{ - ChainA: ChainID(consumerName), - ChainB: ChainID("provi"), - ClientA: 1, - ClientB: 1, - }, - State: State{}, - }, - { - Action: AddIbcChannelAction{ - ChainA: ChainID(consumerName), - ChainB: ChainID("provi"), - ConnectionA: 1, - PortA: "consumer", - PortB: "provider", - Order: "ordered", - }, - State: State{}, - }, - } - - return s -} - -// start sovereign chain with a single validator so it is easier to manage -// when the chain is converted to a consumer chain the validators from the -// consumer chain will be used -// validatoralice is the only validator on the sovereign chain that is in both -// sovereign validator set and consumer validator set -func stepRunSovereignChain() []Step { - return []Step{ - { - Action: StartSovereignChainAction{ - Chain: ChainID("sover"), - Validators: []StartChainValidator{ - {Id: ValidatorID("alice"), Stake: 500000000, Allocation: 10000000000}, - }, - }, - State: State{ - ChainID("sover"): ChainState{ - ValBalances: &map[ValidatorID]uint{ - ValidatorID("alice"): 9500000000, - }, - }, - }, - }, - { - Action: DelegateTokensAction{ - Chain: ChainID("sover"), - From: ValidatorID("alice"), - To: ValidatorID("alice"), - Amount: 11000000, - }, - State: State{ - ChainID("sover"): ChainState{ - ValPowers: &map[ValidatorID]uint{ - ValidatorID("alice"): 511, - ValidatorID("bob"): 0, // does not exist on pre-ccv sover - ValidatorID("carol"): 0, // does not exist on pre-ccv sover - }, - }, - }, - }, - } -} - -// TODO: use args instead of hardcoding -func stepsUpgradeChain() []Step { - return []Step{ - { - Action: UpgradeProposalAction{ - ChainID: ChainID("sover"), - UpgradeTitle: "sovereign-changeover", - Proposer: ValidatorID("alice"), - UpgradeHeight: 110, - Expedited: false, - }, - State: State{ - ChainID("sover"): ChainState{ - Proposals: &map[uint]Proposal{ - 1: UpgradeProposal{ - Title: "sovereign-changeover", - UpgradeHeight: 110, - Type: "/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal", - Deposit: 10000000, - Status: gov.ProposalStatus_PROPOSAL_STATUS_VOTING_PERIOD.String(), - }, - }, - }, - }, - }, - { - Action: VoteGovProposalAction{ - Chain: ChainID("sover"), - From: []ValidatorID{ValidatorID("alice")}, - Vote: []string{"yes"}, - PropNumber: 1, - }, - State: State{ - ChainID("sover"): ChainState{ - Proposals: &map[uint]Proposal{ - 1: UpgradeProposal{ - Deposit: 10000000, - UpgradeHeight: 110, - Title: "sovereign-changeover", - Type: "/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal", - Status: gov.ProposalStatus_PROPOSAL_STATUS_PASSED.String(), - }, - }, - }, - }, - }, - { - Action: WaitUntilBlockAction{ - Chain: ChainID("sover"), - Block: 110, - }, - State: State{}, - }, - } -} - -// stepsPostChangeoverDelegate tests basic delegation and resulting validator power changes after changeover -// we cannot use stepsDelegate and stepsUnbond because they make assumptions about which connection to use -// here we need to use connection-1, and in tests with new consumers connection-0 is used because the chain is new (has no IBC states prior to launch) -func stepsPostChangeoverDelegate(consumerName string) []Step { - return []Step{ - { - Action: SendTokensAction{ - Chain: ChainID(consumerName), - From: ValidatorID("alice"), - To: ValidatorID("bob"), - Amount: 100, - }, - State: State{ - ChainID(consumerName): ChainState{ - // Tx should not go through, ICS channel is not setup until first VSC packet has been relayed to consumer - ValBalances: &map[ValidatorID]uint{ - ValidatorID("bob"): 0, - }, - }, - }, - }, - { - Action: DelegateTokensAction{ - Chain: ChainID("provi"), - From: ValidatorID("alice"), - To: ValidatorID("alice"), - Amount: 11000000, - }, - State: State{ - ChainID("provi"): ChainState{ - ValPowers: &map[ValidatorID]uint{ - ValidatorID("alice"): 511, - ValidatorID("bob"): 500, - ValidatorID("carol"): 500, - }, - }, - ChainID(consumerName): ChainState{ - ValPowers: &map[ValidatorID]uint{ - ValidatorID("alice"): 500, - ValidatorID("bob"): 500, - ValidatorID("carol"): 500, - }, - }, - }, - }, - { - Action: RelayPacketsAction{ - ChainA: ChainID("provi"), - ChainB: ChainID(consumerName), - Port: "provider", - Channel: 1, - }, - State: State{ - ChainID(consumerName): ChainState{ - ValPowers: &map[ValidatorID]uint{ - ValidatorID("alice"): 511, - ValidatorID("bob"): 500, - ValidatorID("carol"): 500, - }, - }, - }, - }, - { - Action: SendTokensAction{ - Chain: ChainID(consumerName), - From: ValidatorID("alice"), - To: ValidatorID("bob"), - Amount: 100, - }, - State: State{ - ChainID(consumerName): ChainState{ - // Tx should go through, ICS channel is setup - ValBalances: &map[ValidatorID]uint{ - ValidatorID("bob"): 100, - }, - }, - }, - }, - { - Action: UnbondTokensAction{ - Chain: ChainID("provi"), - UnbondFrom: ValidatorID("alice"), - Sender: ValidatorID("alice"), - Amount: 1000000, - }, - State: State{ - ChainID("provi"): ChainState{ - ValPowers: &map[ValidatorID]uint{ - ValidatorID("alice"): 510, - ValidatorID("bob"): 500, - ValidatorID("carol"): 500, - }, - }, - ChainID(consumerName): ChainState{ - ValPowers: &map[ValidatorID]uint{ - // Voting power on consumer should not be affected yet - ValidatorID("alice"): 511, - ValidatorID("bob"): 500, - ValidatorID("carol"): 500, - }, - }, - }, - }, - { - Action: RelayPacketsAction{ - ChainA: ChainID("provi"), - ChainB: ChainID(consumerName), - Port: "provider", - Channel: 1, - }, - State: State{ - ChainID(consumerName): ChainState{ - ValPowers: &map[ValidatorID]uint{ - ValidatorID("alice"): 510, - ValidatorID("bob"): 500, - ValidatorID("carol"): 500, - }, - }, - }, - }, - } -} diff --git a/tests/e2e/test_driver.go b/tests/e2e/test_driver.go index 3b918912ae..d3ed0be9e4 100644 --- a/tests/e2e/test_driver.go +++ b/tests/e2e/test_driver.go @@ -297,18 +297,6 @@ func (td *DefaultDriver) runAction(action interface{}) error { case StartChainAction: target := td.getTargetDriver(action.Chain) target.StartChain(action, td.verbose) - case StartSovereignChainAction: - target := td.getTargetDriver(action.Chain) - target.startSovereignChain(action, td.verbose) - case UpgradeProposalAction: - target := td.getTargetDriver(ChainID("sover")) - target.submitUpgradeProposal(action, td.verbose) - case WaitUntilBlockAction: - target := td.getTargetDriver(action.Chain) - target.waitUntilBlockOnChain(action) - case e2e.ChangeoverChainAction: - target := td.getTargetDriver("") - target.changeoverChain(action, td.verbose) case SendTokensAction: target := td.getTargetDriver(action.Chain) target.sendTokens(action, td.verbose) diff --git a/tests/e2e/testlib/types.go b/tests/e2e/testlib/types.go index f5fce3df09..6a290a2483 100644 --- a/tests/e2e/testlib/types.go +++ b/tests/e2e/testlib/types.go @@ -76,20 +76,6 @@ type StartConsumerChainAction struct { GenesisChanges string } -type ChangeoverChainAction struct { - SovereignChain ChainID - ProviderChain ChainID - Validators []StartChainValidator - GenesisChanges string -} - -type StartSovereignChainAction struct { - Chain ChainID - Validators []StartChainValidator - // Genesis changes specific to this action, appended to genesis changes defined in chain config - GenesisChanges string -} - type DelegateTokensAction struct { Chain ChainID From ValidatorID diff --git a/tests/e2e/testnet-scripts/sovereign-genesis.json b/tests/e2e/testnet-scripts/sovereign-genesis.json deleted file mode 100644 index bf7c881b1d..0000000000 --- a/tests/e2e/testnet-scripts/sovereign-genesis.json +++ /dev/null @@ -1,315 +0,0 @@ -{ - "genesis_time": "2023-06-22T15:55:00.982713586Z", - "chain_id": "sover", - "initial_height": "1", - "consensus_params": { - "block": { - "max_bytes": "22020096", - "max_gas": "-1" - }, - "evidence": { - "max_age_num_blocks": "100000", - "max_age_duration": "172800000000000", - "max_bytes": "1048576" - }, - "validator": { - "pub_key_types": [ - "ed25519" - ] - }, - "version": { - "app": "0" - } - }, - "app_hash": "", - "app_state": { - "07-tendermint": null, - "auth": { - "params": { - "max_memo_characters": "256", - "tx_sig_limit": "7", - "tx_size_cost_per_byte": "10", - "sig_verify_cost_ed25519": "590", - "sig_verify_cost_secp256k1": "1000" - }, - "accounts": [ - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "consumer19pe9pg5dv9k5fzgzmsrgnw9rl9asf7ddtz33vu", - "pub_key": null, - "account_number": "0", - "sequence": "0" - } - ] - }, - "authz": { - "authorization": [] - }, - "bank": { - "params": { - "send_enabled": [], - "default_send_enabled": true - }, - "balances": [ - { - "address": "consumer19pe9pg5dv9k5fzgzmsrgnw9rl9asf7ddtz33vu", - "coins": [ - { - "denom": "stake", - "amount": "10000000000" - } - ] - } - ], - "supply": [ - { - "denom": "stake", - "amount": "10000000000" - } - ], - "denom_metadata": [], - "send_enabled": [] - }, - "capability": { - "index": "1", - "owners": [] - }, - "crisis": { - "constant_fee": { - "denom": "stake", - "amount": "1000" - } - }, - "distribution": { - "params": { - "community_tax": "0.020000000000000000", - "base_proposer_reward": "0.000000000000000000", - "bonus_proposer_reward": "0.000000000000000000", - "withdraw_addr_enabled": true - }, - "fee_pool": { - "community_pool": [] - }, - "delegator_withdraw_infos": [], - "previous_proposer": "", - "outstanding_rewards": [], - "validator_accumulated_commissions": [], - "validator_historical_rewards": [], - "validator_current_rewards": [], - "delegator_starting_infos": [], - "validator_slash_events": [] - }, - "evidence": { - "evidence": [] - }, - "feegrant": { - "allowances": [] - }, - "genutil": { - "gen_txs": [ - { - "body": { - "messages": [ - { - "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", - "description": { - "moniker": "validatoralice", - "identity": "", - "website": "", - "security_contact": "", - "details": "" - }, - "commission": { - "rate": "0.100000000000000000", - "max_rate": "0.200000000000000000", - "max_change_rate": "0.010000000000000000" - }, - "min_self_delegation": "1", - "delegator_address": "consumer19pe9pg5dv9k5fzgzmsrgnw9rl9asf7ddtz33vu", - "validator_address": "consumervaloper19pe9pg5dv9k5fzgzmsrgnw9rl9asf7ddy6jwzg", - "pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "RrclQz9bIhkIy/gfL485g3PYMeiIku4qeo495787X10=" - }, - "value": { - "denom": "stake", - "amount": "500000000" - } - } - ], - "memo": "8339e14baab81c2a2350e261962263397a8d7fb0@7.7.7.254:26656", - "timeout_height": "0", - "extension_options": [], - "non_critical_extension_options": [] - }, - "auth_info": { - "signer_infos": [ - { - "public_key": { - "@type": "/cosmos.crypto.secp256k1.PubKey", - "key": "AsFC8tmbGGQSHthsVStbsQ13/+Yza9IT8KCSXXEN7y9f" - }, - "mode_info": { - "single": { - "mode": "SIGN_MODE_DIRECT" - } - }, - "sequence": "0" - } - ], - "fee": { - "amount": [], - "gas_limit": "200000", - "payer": "", - "granter": "" - }, - "tip": null - }, - "signatures": [ - "AZROMEeaBL9cDOWQJYYdAG3KDl+w37SK0XP88ecS+WwQQLj8rXuEKNDl1PXpZR0AFIJ8coSwhFEtbpV44j6uVQ==" - ] - } - ] - }, - "gov": { - "starting_proposal_id": "1", - "deposits": [], - "votes": [], - "proposals": [], - "deposit_params": null, - "voting_params": null, - "tally_params": null, - "params": { - "min_deposit": [ - { - "denom": "stake", - "amount": "10000000" - } - ], - "max_deposit_period": "172800s", - "voting_period": "20s", - "quorum": "0.334000000000000000", - "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, - "min_deposit_ratio": "0.010000000000000000" - } - }, - "ibc": { - "client_genesis": { - "clients": [], - "clients_consensus": [], - "clients_metadata": [], - "params": { - "allowed_clients": [ - "06-solomachine", - "07-tendermint", - "09-localhost" - ] - }, - "create_localhost": false, - "next_client_sequence": "0" - }, - "connection_genesis": { - "connections": [], - "client_connection_paths": [], - "next_connection_sequence": "0", - "params": { - "max_expected_time_per_block": "30000000000" - } - }, - "channel_genesis": { - "channels": [], - "acknowledgements": [], - "commitments": [], - "receipts": [], - "send_sequences": [], - "recv_sequences": [], - "ack_sequences": [], - "next_channel_sequence": "0", - "params": { - "upgrade_timeout": { - "height": { - "revision_number": "0", - "revision_height": "0" - }, - "timestamp": "600000000000" - } - } - } - }, - "mint": { - "minter": { - "inflation": "0.130000000000000000", - "annual_provisions": "0.000000000000000000" - }, - "params": { - "mint_denom": "stake", - "inflation_rate_change": "0.130000000000000000", - "inflation_max": "0.200000000000000000", - "inflation_min": "0.070000000000000000", - "goal_bonded": "0.670000000000000000", - "blocks_per_year": "6311520" - } - }, - "params": null, - "provider": { - "params": { - "slash_meter_replenish_fraction": "1.0", - "slash_meter_replenish_period": "3s" - } - }, - "slashing": { - "params": { - "signed_blocks_window": "10", - "min_signed_per_window": "0.500000000000000000", - "downtime_jail_duration": "60s", - "slash_fraction_double_sign": "0.050000000000000000", - "slash_fraction_downtime": "0.010000000000000000" - }, - "signing_infos": [], - "missed_blocks": [] - }, - "staking": { - "params": { - "unbonding_time": "1814400s", - "max_validators": 100, - "max_entries": 7, - "historical_entries": 10000, - "bond_denom": "stake", - "min_commission_rate": "0.000000000000000000" - }, - "last_total_power": "0", - "last_validator_powers": [], - "validators": [], - "delegations": [], - "unbonding_delegations": [], - "redelegations": [], - "exported": false - }, - "transfer": { - "port_id": "transfer", - "denom_traces": [], - "params": { - "send_enabled": true, - "receive_enabled": true - }, - "total_escrowed": [] - }, - "upgrade": {}, - "vesting": {} - } -} \ No newline at end of file diff --git a/tests/e2e/testnet-scripts/start-changeover.sh b/tests/e2e/testnet-scripts/start-changeover.sh deleted file mode 100644 index 6a134ac4dc..0000000000 --- a/tests/e2e/testnet-scripts/start-changeover.sh +++ /dev/null @@ -1,189 +0,0 @@ -#!/bin/bash -set -eux - -# The gaiad binary -BIN=$1 - -# JSON array of validator information -# [{ -# mnemonic: "crackle snap pop ... etc", -# allocation: "10000000000stake,10000000000footoken", -# stake: "5000000000stake", -# val_id: "alice", -# ip_suffix: "1", -# priv_validator_key: "{\"address\": \"3566F464673B2F069758DAE86FC25D04017BB147\",\"pub_key\": {\"type\": \"tendermint/PubKeyEd25519\",\"value\": \"XrLjKdc4mB2gfqplvnoySjSJq2E90RynUwaO3WhJutk=\"},\"priv_key\": {\"type\": \"tendermint/PrivKeyEd25519\",\"value\": \"czGSLs/Ocau8aJ5J5zQHMxf3d7NR0xjMECN6YGTIWqtesuMp1ziYHaB+qmW+ejJKNImrYT3RHKdTBo7daEm62Q==\"}}" -# node_key: "{\"priv_key\":{\"type\":\"tendermint/PrivKeyEd25519\",\"value\":\"alIHj6hXnzpLAadgb7+E2eeecwxoNdzuZrfhMX36EaD5/LgzL0ZUoVp7AK3np0K5T35JWLLv0jJKmeRIhG0GjA==\"}}" -# }, ... ] -VALIDATORS=$2 - -# The chain ID -CHAIN_ID=$3 - -# This is the first 3 fields of the IP addresses which will be used internally by the validators of this blockchain -# Recommended to use something starting with 7, since it is squatted by the DoD and is unroutable on the internet -# For example: "7.7.7" -CHAIN_IP_PREFIX=$4 - -# A transformation to apply to the genesis file, as a jq string -GENESIS_TRANSFORM=$5 - -# A sed string modifying the tendermint config -TENDERMINT_CONFIG_TRANSFORM=$6 - - -# CREATE VALIDATORS AND DO GENESIS CEREMONY -# !!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!!! # -# data dir from the sovereign chain is copied to other nodes (namely bob and carol) -# alice simply performs a chain upgrade -echo "killing nodes" -pkill -f "^"interchain-security-sd &> /dev/null || true - -# Get number of nodes from length of validators array -NODES=$(echo "$VALIDATORS" | jq '. | length') - -for i in $(seq 0 $(($NODES - 1))); -do - VAL_ID=$(echo "$VALIDATORS" | jq -r ".[$i].val_id") - mkdir -p /$CHAIN_ID/validator$VAL_ID/.sovereign/config - # apply genesis changes to existing genesis -> this creates the changeover genesis file with initial validator set - jq "$GENESIS_TRANSFORM" /sover/validatoralice/config/genesis.json > /$CHAIN_ID/validator$VAL_ID/.sovereign/config/genesis.json -done - -# SETUP NETWORK NAMESPACES, see: https://adil.medium.com/container-networking-under-the-hood-network-namespaces-6b2b8fe8dc2a - -# Create virtual bridge device (acts like a switch) -ip link add name virtual-bridge type bridge || true - -for i in $(seq 0 $(($NODES - 1))); -do - # first validator is already setup - if [[ "$i" -ne 0 ]]; then - VAL_ID=$(echo "$VALIDATORS" | jq -r ".[$i].val_id") - VAL_IP_SUFFIX=$(echo "$VALIDATORS" | jq -r ".[$i].ip_suffix") - NET_NAMESPACE_NAME="$CHAIN_ID-$VAL_ID" - IP_ADDR="$CHAIN_IP_PREFIX.$VAL_IP_SUFFIX/24" - - # Create network namespace - ip netns add $NET_NAMESPACE_NAME - # Create virtual ethernet device to connect with bridge - ip link add $NET_NAMESPACE_NAME-in type veth peer name $NET_NAMESPACE_NAME-out - # Connect input end of virtual ethernet device to namespace - ip link set $NET_NAMESPACE_NAME-in netns $NET_NAMESPACE_NAME - # Assign ip address to namespace - ip netns exec $NET_NAMESPACE_NAME ip addr add $IP_ADDR dev $NET_NAMESPACE_NAME-in - # Connect output end of virtual ethernet device to bridge - ip link set $NET_NAMESPACE_NAME-out master virtual-bridge - fi -done - -# Enable bridge interface -ip link set virtual-bridge up - -for i in $(seq 0 $(($NODES - 1))); -do - # first validator is already setup - if [[ "$i" -ne 0 ]]; then - VAL_ID=$(echo "$VALIDATORS" | jq -r ".[$i].val_id") - NET_NAMESPACE_NAME="$CHAIN_ID-$VAL_ID" - - # Enable in/out interfaces for the namespace - ip link set $NET_NAMESPACE_NAME-out up - ip netns exec $NET_NAMESPACE_NAME ip link set dev $NET_NAMESPACE_NAME-in up - # Enable loopback device - ip netns exec $NET_NAMESPACE_NAME ip link set dev lo up - fi -done - -# Assign IP for bridge, to route between default network namespace and bridge -# BRIDGE_IP="$CHAIN_IP_PREFIX.254/24" -# ip addr add $BRIDGE_IP dev virtual-bridge - - -# HANDLE VALIDATOR HOMES, COPY OLD DATA FOLDER -for i in $(seq 0 $(($NODES - 1))); -do - # first validator is already setup - if [[ "$i" -ne 0 ]]; then - VAL_ID=$(echo "$VALIDATORS" | jq -r ".[$i].val_id") - echo "$VALIDATORS" | jq -r ".[$i].mnemonic" | $BIN keys add validator$VAL_ID \ - --home /$CHAIN_ID/validator$VAL_ID \ - --keyring-backend test \ - --recover > /dev/null - - # Copy in the genesis.json - cp /sover/validatoralice/config/genesis.json /$CHAIN_ID/validator$VAL_ID/config/genesis.json - cp -r /sover/validatoralice/data /$CHAIN_ID/validator$VAL_ID/ - - # Copy in validator state file - # echo '{"height": "0","round": 0,"step": 0}' > /$CHAIN_ID/validator$VAL_ID/data/priv_validator_state.json - - - PRIV_VALIDATOR_KEY=$(echo "$VALIDATORS" | jq -r ".[$i].priv_validator_key") - if [[ "$PRIV_VALIDATOR_KEY" ]]; then - echo "$PRIV_VALIDATOR_KEY" > /$CHAIN_ID/validator$VAL_ID/config/priv_validator_key.json - fi - - NODE_KEY=$(echo "$VALIDATORS" | jq -r ".[$i].node_key") - if [[ "$NODE_KEY" ]]; then - echo "$NODE_KEY" > /$CHAIN_ID/validator$VAL_ID/config/node_key.json - fi - - # Modify tendermint configs of validator - if [ "$TENDERMINT_CONFIG_TRANSFORM" != "" ] ; then - #'s/foo/bar/;s/abc/def/' - sed -i "$TENDERMINT_CONFIG_TRANSFORM" $CHAIN_ID/validator$VAL_ID/config/config.toml - fi - fi -done - - -# START VALIDATOR NODES -> this will perform the sovereign upgrade and start the chain -# ALICE is a validator on the sovereign and also the validator on the consumer chain -# BOB, CAROL are not validators on the sovereign, they will become validator once the chain switches to the consumer chain -echo "Starting validator nodes..." -for i in $(seq 0 $(($NODES - 1))); -do - VAL_ID=$(echo "$VALIDATORS" | jq -r ".[$i].val_id") - VAL_IP_SUFFIX=$(echo "$VALIDATORS" | jq -r ".[$i].ip_suffix") - NET_NAMESPACE_NAME="$CHAIN_ID-$VAL_ID" - - GAIA_HOME="--home /$CHAIN_ID/validator$VAL_ID" - RPC_ADDRESS="--rpc.laddr tcp://$CHAIN_IP_PREFIX.$VAL_IP_SUFFIX:26658" - GRPC_ADDRESS="--grpc.address $CHAIN_IP_PREFIX.$VAL_IP_SUFFIX:9091" - LISTEN_ADDRESS="--address tcp://$CHAIN_IP_PREFIX.$VAL_IP_SUFFIX:26655" - P2P_ADDRESS="--p2p.laddr tcp://$CHAIN_IP_PREFIX.$VAL_IP_SUFFIX:26656" - # LOG_LEVEL="--log_level trace" # switch to trace to see panic messages and rich and all debug msgs - LOG_LEVEL="--log_level info" - ENABLE_WEBGRPC="--grpc-web.enable=false" - - PERSISTENT_PEERS="" - - for j in $(seq 0 $(($NODES - 1))); - do - if [ $i -ne $j ]; then - PEER_VAL_ID=$(echo "$VALIDATORS" | jq -r ".[$j].val_id") - PEER_VAL_IP_SUFFIX=$(echo "$VALIDATORS" | jq -r ".[$j].ip_suffix") - NODE_ID=$($BIN tendermint show-node-id --home /$CHAIN_ID/validator$PEER_VAL_ID) - ADDRESS="$NODE_ID@$CHAIN_IP_PREFIX.$PEER_VAL_IP_SUFFIX:26656" - # (jq -r '.body.memo' /$CHAIN_ID/validator$j/config/gentx/*) # Getting the address from the gentx should also work - PERSISTENT_PEERS="$PERSISTENT_PEERS,$ADDRESS" - fi - done - - # Remove leading comma and concat to flag - PERSISTENT_PEERS="--p2p.persistent_peers ${PERSISTENT_PEERS:1}" - - ARGS="$GAIA_HOME $LISTEN_ADDRESS $RPC_ADDRESS $GRPC_ADDRESS $LOG_LEVEL $P2P_ADDRESS $ENABLE_WEBGRPC $PERSISTENT_PEERS" - ip netns exec $NET_NAMESPACE_NAME $BIN $ARGS start &> /$CHAIN_ID/validator$VAL_ID/logs & -done - -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 --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!!!!!!!!" - -read -p "Press Return to Close..." diff --git a/tests/e2e/testnet-scripts/start-sovereign.sh b/tests/e2e/testnet-scripts/start-sovereign.sh deleted file mode 100644 index 54e14dfcf3..0000000000 --- a/tests/e2e/testnet-scripts/start-sovereign.sh +++ /dev/null @@ -1,133 +0,0 @@ -#!/bin/bash -set -eux - -# The gaiad binary -BIN=$1 - -# JSON array of validator information -# [{ -# mnemonic: "crackle snap pop ... etc", -# allocation: "10000000000stake,10000000000footoken", -# stake: "5000000000stake", -# val_id: "alice", -# ip_suffix: "1", -# priv_validator_key: "{\"address\": \"3566F464673B2F069758DAE86FC25D04017BB147\",\"pub_key\": {\"type\": \"tendermint/PubKeyEd25519\",\"value\": \"XrLjKdc4mB2gfqplvnoySjSJq2E90RynUwaO3WhJutk=\"},\"priv_key\": {\"type\": \"tendermint/PrivKeyEd25519\",\"value\": \"czGSLs/Ocau8aJ5J5zQHMxf3d7NR0xjMECN6YGTIWqtesuMp1ziYHaB+qmW+ejJKNImrYT3RHKdTBo7daEm62Q==\"}}" -# node_key: "{\"priv_key\":{\"type\":\"tendermint/PrivKeyEd25519\",\"value\":\"alIHj6hXnzpLAadgb7+E2eeecwxoNdzuZrfhMX36EaD5/LgzL0ZUoVp7AK3np0K5T35JWLLv0jJKmeRIhG0GjA==\"}}" -# }, ... ] -VALIDATORS=$2 - -# The chain ID -CHAIN_ID=$3 - -# This is the first 3 fields of the IP addresses which will be used internally by the validators of this blockchain -# Recommended to use something starting with 7, since it is squatted by the DoD and is unroutable on the internet -# For example: "7.7.7" -CHAIN_IP_PREFIX=$4 - -# A transformation to apply to the genesis file, as a jq string -GENESIS_TRANSFORM=$5 - -# A sed string modifying the tendermint config -TENDERMINT_CONFIG_TRANSFORM=$6 - -# SETUP NETWORK NAMESPACES, see: https://adil.medium.com/container-networking-under-the-hood-network-namespaces-6b2b8fe8dc2a - -# Create virtual bridge device (acts like a switch) -ip link add name virtual-bridge type bridge || true - -# used globally in the whole script -VAL_ID=$(echo "$VALIDATORS" | jq -r ".[0].val_id") -VAL_IP_SUFFIX=$(echo "$VALIDATORS" | jq -r ".[0].ip_suffix") -NET_NAMESPACE_NAME="$CHAIN_ID-$VAL_ID" -IP_ADDR="$CHAIN_IP_PREFIX.$VAL_IP_SUFFIX/24" - -# Create network namespace -ip netns add $NET_NAMESPACE_NAME -# Create virtual ethernet device to connect with bridge -ip link add $NET_NAMESPACE_NAME-in type veth peer name $NET_NAMESPACE_NAME-out -# Connect input end of virtual ethernet device to namespace -ip link set $NET_NAMESPACE_NAME-in netns $NET_NAMESPACE_NAME -# Assign ip address to namespace -ip netns exec $NET_NAMESPACE_NAME ip addr add $IP_ADDR dev $NET_NAMESPACE_NAME-in -# Connect output end of virtual ethernet device to bridge -ip link set $NET_NAMESPACE_NAME-out master virtual-bridge - -# Enable bridge interface -ip link set virtual-bridge up - -NET_NAMESPACE_NAME="$CHAIN_ID-$VAL_ID" -# Enable in/out interfaces for the namespace -ip link set $NET_NAMESPACE_NAME-out up -ip netns exec $NET_NAMESPACE_NAME ip link set dev $NET_NAMESPACE_NAME-in up -# Enable loopback device -ip netns exec $NET_NAMESPACE_NAME ip link set dev lo up - -# Assign IP for bridge, to route between default network namespace and bridge -BRIDGE_IP="$CHAIN_IP_PREFIX.254/24" -ip addr add $BRIDGE_IP dev virtual-bridge - -# first we start a genesis.json with the first validator -# the first validator will also collect the gentx's once generated -echo "$VALIDATORS" | jq -r ".[0].mnemonic" | $BIN init --home /$CHAIN_ID/validator$VAL_ID --chain-id=$CHAIN_ID validator$VAL_ID --recover > /dev/null - -# !!!!!!!!! IMPORTANT !!!!!!!!! # -# move the sovereign genesis to the correct validator home dir -cp /testnet-scripts/sovereign-genesis.json /$CHAIN_ID/validator$VAL_ID/config/genesis.json - -# Apply jq transformations to genesis file -jq "$GENESIS_TRANSFORM" /$CHAIN_ID/validator$VAL_ID/config/genesis.json > /$CHAIN_ID/edited-genesis.json -mv /$CHAIN_ID/edited-genesis.json /$CHAIN_ID/genesis.json -cp /$CHAIN_ID/genesis.json /$CHAIN_ID/validator$VAL_ID/config/genesis.json - - - -# SETUP LOCAL VALIDATOR STATE -echo '{"height": "0","round": 0,"step": 0}' > /$CHAIN_ID/validator$VAL_ID/data/priv_validator_state.json - -PRIV_VALIDATOR_KEY=$(echo "$VALIDATORS" | jq -r ".[0].priv_validator_key") -if [[ "$PRIV_VALIDATOR_KEY" ]]; then - echo "$PRIV_VALIDATOR_KEY" > /$CHAIN_ID/validator$VAL_ID/config/priv_validator_key.json -fi - -NODE_KEY=$(echo "$VALIDATORS" | jq -r ".[0].node_key") -if [[ "$NODE_KEY" ]]; then - echo "$NODE_KEY" > /$CHAIN_ID/validator$VAL_ID/config/node_key.json -fi - -echo "$VALIDATORS" | jq -r ".[0].mnemonic" | $BIN keys add validator$VAL_ID \ ---home /$CHAIN_ID/validator$VAL_ID \ ---keyring-backend test \ ---recover > /dev/null - -# Modify tendermint configs of validator -if [ "$TENDERMINT_CONFIG_TRANSFORM" != "" ] ; then - #'s/foo/bar/;s/abc/def/' - sed -i "$TENDERMINT_CONFIG_TRANSFORM" $CHAIN_ID/validator$VAL_ID/config/config.toml -fi - - -# START VALIDATOR NODE -VAL_IP_SUFFIX=$(echo "$VALIDATORS" | jq -r ".[0].ip_suffix") -NET_NAMESPACE_NAME="$CHAIN_ID-$VAL_ID" - -GAIA_HOME="--home /$CHAIN_ID/validator$VAL_ID" -RPC_ADDRESS="--rpc.laddr tcp://$CHAIN_IP_PREFIX.$VAL_IP_SUFFIX:26658" -GRPC_ADDRESS="--grpc.address $CHAIN_IP_PREFIX.$VAL_IP_SUFFIX:9091" -LISTEN_ADDRESS="--address tcp://$CHAIN_IP_PREFIX.$VAL_IP_SUFFIX:26655" -P2P_ADDRESS="--p2p.laddr tcp://$CHAIN_IP_PREFIX.$VAL_IP_SUFFIX:26656" -# LOG_LEVEL="--log_level trace" # switch to trace to see panic messages and rich and all debug msgs -LOG_LEVEL="--log_level info" -ENABLE_WEBGRPC="--grpc-web.enable=false" - -ARGS="$GAIA_HOME $LISTEN_ADDRESS $RPC_ADDRESS $GRPC_ADDRESS $LOG_LEVEL $P2P_ADDRESS $ENABLE_WEBGRPC" -ip netns exec $NET_NAMESPACE_NAME $BIN $ARGS start &> /$CHAIN_ID/validator$VAL_ID/logs & - - -# poll for chain start -set +e -until $BIN query block --type=height 0 --node "tcp://$CHAIN_IP_PREFIX.$VAL_IP_SUFFIX:26658" | grep -q -v '{"block_id":{"hash":"","parts":{"total":0,"hash":""}},"block":null}'; do sleep 0.3 ; done -set -e - -echo "done!!!!!!!!" - -read -p "Press Return to Close..." \ No newline at end of file diff --git a/tests/e2e/trace_handlers_test.go b/tests/e2e/trace_handlers_test.go index fe15656545..77e5d83814 100644 --- a/tests/e2e/trace_handlers_test.go +++ b/tests/e2e/trace_handlers_test.go @@ -51,7 +51,6 @@ func TestWriterThenParser(t *testing.T) { "multipleConsumers": {multipleConsumers}, "shorthappy": {shortHappyPathSteps}, "democracyRewardsSteps": {democracyRegisteredDenomSteps}, - "changeover": {changeoverSteps}, } dir, err := os.MkdirTemp("", "example") @@ -82,7 +81,6 @@ func TestWriteExamples(t *testing.T) { "multipleConsumers": {multipleConsumers}, "shorthappy": {shortHappyPathSteps}, "democracyRewardsSteps": {democracyRegisteredDenomSteps}, - "changeover": {changeoverSteps}, "consumer-misbehaviour": {consumerMisbehaviourSteps}, "consumer-double-sign": {consumerDoubleSignSteps}, } diff --git a/tests/e2e/tracehandler_testdata/changeover.json b/tests/e2e/tracehandler_testdata/changeover.json deleted file mode 100644 index af1f68a91a..0000000000 --- a/tests/e2e/tracehandler_testdata/changeover.json +++ /dev/null @@ -1,659 +0,0 @@ -[ - { - "ActionType": "main.StartSovereignChainAction", - "Action": { - "Chain": "sover", - "Validators": [ - { - "Id": "alice", - "Allocation": 10000000000, - "Stake": 500000000 - } - ], - "GenesisChanges": "" - }, - "State": { - "sover": { - "ValBalances": { - "alice": 9500000000 - }, - "ProposedConsumerChains": null, - "ValPowers": null, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": null - } - } - }, - { - "ActionType": "main.DelegateTokensAction", - "Action": { - "Chain": "sover", - "From": "alice", - "To": "alice", - "Amount": 11000000 - }, - "State": { - "sover": { - "ValBalances": null, - "ProposedConsumerChains": null, - "ValPowers": { - "alice": 511, - "bob": 0, - "carol": 0 - }, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": null - } - } - }, - { - "ActionType": "main.StartChainAction", - "Action": { - "Chain": "provi", - "Validators": [ - { - "Id": "bob", - "Allocation": 10000000000, - "Stake": 500000000 - }, - { - "Id": "alice", - "Allocation": 10000000000, - "Stake": 500000000 - }, - { - "Id": "carol", - "Allocation": 10000000000, - "Stake": 500000000 - } - ], - "GenesisChanges": "", - "IsConsumer": false - }, - "State": { - "provi": { - "ValBalances": { - "alice": 9500000000, - "bob": 9500000000, - "carol": 9500000000 - }, - "ProposedConsumerChains": null, - "ValPowers": null, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": null - } - } - }, - { - "ActionType": "main.CreateIbcClientsAction", - "Action": { - "ChainA": "sover", - "ChainB": "provi" - }, - "State": {} - }, - { - "ActionType": "main.AddIbcChannelAction", - "Action": { - "ChainA": "sover", - "ChainB": "provi", - "ConnectionA": 0, - "PortA": "transfer", - "PortB": "transfer", - "Order": "unordered", - "Version": "ics20-1" - }, - "State": {} - }, - { - "ActionType": "main.LegacyUpgradeProposalAction", - "Action": { - "ChainID": "sover", - "UpgradeTitle": "sovereign-changeover", - "Proposer": "alice", - "UpgradeHeight": 110 - }, - "State": { - "sover": { - "ValBalances": null, - "ProposedConsumerChains": null, - "ValPowers": null, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": { - "1": { - "RawProposal": { - "Title": "sovereign-changeover", - "Description": "", - "UpgradeHeight": 110, - "Type": "/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal", - "Deposit": 10000000, - "Status": "2" - }, - "Type": "e2e.UpgradeProposal" - } - } - } - } - }, - { - "ActionType": "main.VoteGovProposalAction", - "Action": { - "Chain": "sover", - "From": [ - "alice" - ], - "Vote": [ - "yes" - ], - "PropNumber": 1 - }, - "State": { - "sover": { - "ValBalances": null, - "ProposedConsumerChains": null, - "ValPowers": null, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": { - "1": { - "RawProposal": { - "Title": "sovereign-changeover", - "Description": "", - "UpgradeHeight": 110, - "Type": "/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal", - "Deposit": 10000000, - "Status": "3" - }, - "Type": "e2e.UpgradeProposal" - } - } - } - } - }, - { - "ActionType": "main.WaitUntilBlockAction", - "Action": { - "Block": 110, - "Chain": "sover" - }, - "State": {} - }, - { - "ActionType": "main.SubmitConsumerAdditionProposalAction", - "Action": { - "PreCCV": true, - "Chain": "provi", - "From": "alice", - "Deposit": 10000001, - "ConsumerChain": "sover", - "SpawnTime": 0, - "InitialHeight": { - "revision_height": 111 - }, - "DistributionChannel": "channel-0", - "TopN": 100, - "ValidatorsPowerCap": 0, - "ValidatorSetCap": 0, - "Allowlist": null, - "Denylist": null - }, - "State": { - "provi": { - "ValBalances": { - "alice": 9489999999, - "bob": 9500000000 - }, - "ProposedConsumerChains": null, - "ValPowers": null, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": { - "1": { - "RawProposal": { - "Deposit": 10000001, - "Chain": "sover", - "SpawnTime": 0, - "InitialHeight": { - "revision_height": 111 - }, - "Status": "2" - }, - "Type": "e2e.ConsumerAdditionProposal" - } - } - } - } - }, - { - "ActionType": "main.VoteGovProposalAction", - "Action": { - "Chain": "provi", - "From": [ - "alice", - "bob", - "carol" - ], - "Vote": [ - "yes", - "yes", - "yes" - ], - "PropNumber": 1 - }, - "State": { - "provi": { - "ValBalances": { - "alice": 9500000000, - "bob": 9500000000 - }, - "ProposedConsumerChains": null, - "ValPowers": null, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": { - "1": { - "RawProposal": { - "Deposit": 10000001, - "Chain": "sover", - "SpawnTime": 0, - "InitialHeight": { - "revision_height": 111 - }, - "Status": "3" - }, - "Type": "e2e.ConsumerAdditionProposal" - } - } - } - } - }, - { - "ActionType": "main.ChangeoverChainAction", - "Action": { - "SovereignChain": "sover", - "ProviderChain": "provi", - "Validators": [ - { - "Id": "alice", - "Allocation": 10000000000, - "Stake": 500000000 - }, - { - "Id": "bob", - "Allocation": 10000000000, - "Stake": 500000000 - }, - { - "Id": "carol", - "Allocation": 10000000000, - "Stake": 500000000 - } - ], - "GenesisChanges": "" - }, - "State": { - "provi": { - "ValBalances": null, - "ProposedConsumerChains": null, - "ValPowers": { - "alice": 500, - "bob": 500, - "carol": 500 - }, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": null - }, - "sover": { - "ValBalances": null, - "ProposedConsumerChains": null, - "ValPowers": { - "alice": 500, - "bob": 500, - "carol": 500 - }, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": null - } - } - }, - { - "ActionType": "main.AddIbcConnectionAction", - "Action": { - "ChainA": "sover", - "ChainB": "provi", - "ClientA": 1, - "ClientB": 1 - }, - "State": {} - }, - { - "ActionType": "main.AddIbcChannelAction", - "Action": { - "ChainA": "sover", - "ChainB": "provi", - "ConnectionA": 1, - "PortA": "consumer", - "PortB": "provider", - "Order": "ordered", - "Version": "" - }, - "State": {} - }, - { - "ActionType": "main.SendTokensAction", - "Action": { - "Chain": "sover", - "From": "alice", - "To": "bob", - "Amount": 100 - }, - "State": { - "sover": { - "ValBalances": { - "bob": 0 - }, - "ProposedConsumerChains": null, - "ValPowers": null, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": null - } - } - }, - { - "ActionType": "main.DelegateTokensAction", - "Action": { - "Chain": "provi", - "From": "alice", - "To": "alice", - "Amount": 11000000 - }, - "State": { - "provi": { - "ValBalances": null, - "ProposedConsumerChains": null, - "ValPowers": { - "alice": 511, - "bob": 500, - "carol": 500 - }, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": null - }, - "sover": { - "ValBalances": null, - "ProposedConsumerChains": null, - "ValPowers": { - "alice": 500, - "bob": 500, - "carol": 500 - }, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": null - } - } - }, - { - "ActionType": "main.RelayPacketsAction", - "Action": { - "ChainA": "provi", - "ChainB": "sover", - "Port": "provider", - "Channel": 1 - }, - "State": { - "sover": { - "ValBalances": null, - "ProposedConsumerChains": null, - "ValPowers": { - "alice": 511, - "bob": 500, - "carol": 500 - }, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": null - } - } - }, - { - "ActionType": "main.SendTokensAction", - "Action": { - "Chain": "sover", - "From": "alice", - "To": "bob", - "Amount": 100 - }, - "State": { - "sover": { - "ValBalances": { - "bob": 100 - }, - "ProposedConsumerChains": null, - "ValPowers": null, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": null - } - } - }, - { - "ActionType": "main.UnbondTokensAction", - "Action": { - "Chain": "provi", - "Sender": "alice", - "UnbondFrom": "alice", - "Amount": 1000000 - }, - "State": { - "provi": { - "ValBalances": null, - "ProposedConsumerChains": null, - "ValPowers": { - "alice": 510, - "bob": 500, - "carol": 500 - }, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": null - }, - "sover": { - "ValBalances": null, - "ProposedConsumerChains": null, - "ValPowers": { - "alice": 511, - "bob": 500, - "carol": 500 - }, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": null - } - } - }, - { - "ActionType": "main.RelayPacketsAction", - "Action": { - "ChainA": "provi", - "ChainB": "sover", - "Port": "provider", - "Channel": 1 - }, - "State": { - "sover": { - "ValBalances": null, - "ProposedConsumerChains": null, - "ValPowers": { - "alice": 510, - "bob": 500, - "carol": 500 - }, - "StakedTokens": null, - "IBCTransferParams": null, - "Params": null, - "Rewards": null, - "ConsumerChains": null, - "AssignedKeys": null, - "ProviderKeys": null, - "ConsumerPendingPacketQueueSize": null, - "RegisteredConsumerRewardDenoms": null, - "ClientsFrozenHeights": null, - "HasToValidate": null, - "Proposals": null - } - } - } -] \ No newline at end of file diff --git a/tests/e2e/v5/actions.go b/tests/e2e/v5/actions.go index 8dd3a5f1ce..fa206eddaa 100644 --- a/tests/e2e/v5/actions.go +++ b/tests/e2e/v5/actions.go @@ -43,10 +43,8 @@ type ( StartChainAction = e2e.StartChainAction StartChainValidator = e2e.StartChainValidator StartConsumerChainAction = e2e.StartConsumerChainAction - StartSovereignChainAction = e2e.StartSovereignChainAction SubmitConsumerRemovalProposalAction = e2e.SubmitConsumerRemovalProposalAction DelegateTokensAction = e2e.DelegateTokensAction - ChangeoverChainAction = e2e.ChangeoverChainAction UnbondTokensAction = e2e.UnbondTokensAction ) @@ -779,131 +777,6 @@ func (tr *Chain) transformConsumerGenesis(consumerChain ChainID, genesis []byte) return result } -func (tr Chain) changeoverChain( - action ChangeoverChainAction, - verbose bool, -) { - // sleep until the consumer chain genesis is ready on consumer - time.Sleep(5 * time.Second) - cmd := tr.Target.ExecCommand( - tr.TestConfig.ChainConfigs[action.ProviderChain].BinaryName, - - "query", "provider", "consumer-genesis", - string(tr.TestConfig.ChainConfigs[action.SovereignChain].ChainId), - - `--node`, tr.Target.GetQueryNode(action.ProviderChain), - `-o`, `json`, - ) - - if verbose { - log.Println("changeoverChain cmd: ", cmd.String()) - } - - bz, err := cmd.CombinedOutput() - if err != nil { - log.Fatal(err, "\n", string(bz)) - } - - consumerGenesis := ".app_state.ccvconsumer = " + string(bz) - consumerGenesisChanges := tr.TestConfig.ChainConfigs[action.SovereignChain].GenesisChanges - if consumerGenesisChanges != "" { - consumerGenesis = consumerGenesis + " | " + consumerGenesisChanges - } - if action.GenesisChanges != "" { - consumerGenesis = consumerGenesis + " | " + action.GenesisChanges - } - - tr.startChangeover(ChangeoverChainAction{ - Validators: action.Validators, - GenesisChanges: consumerGenesis, - }, verbose) -} - -func (tr Chain) startChangeover( - action ChangeoverChainAction, - verbose bool, -) { - chainConfig := tr.TestConfig.ChainConfigs[ChainID("sover")] - type jsonValAttrs struct { - Mnemonic string `json:"mnemonic"` - Allocation string `json:"allocation"` - Stake string `json:"stake"` - ValId string `json:"val_id"` - PrivValidatorKey string `json:"priv_validator_key"` - NodeKey string `json:"node_key"` - IpSuffix string `json:"ip_suffix"` - - ConsumerMnemonic string `json:"consumer_mnemonic"` - ConsumerPrivValidatorKey string `json:"consumer_priv_validator_key"` - StartWithConsumerKey bool `json:"start_with_consumer_key"` - } - - var validators []jsonValAttrs - for _, val := range action.Validators { - validators = append(validators, jsonValAttrs{ - Mnemonic: tr.TestConfig.ValidatorConfigs[val.Id].Mnemonic, - NodeKey: tr.TestConfig.ValidatorConfigs[val.Id].NodeKey, - ValId: fmt.Sprint(val.Id), - PrivValidatorKey: tr.TestConfig.ValidatorConfigs[val.Id].PrivValidatorKey, - Allocation: fmt.Sprint(val.Allocation) + "stake", - Stake: fmt.Sprint(val.Stake) + "stake", - IpSuffix: tr.TestConfig.ValidatorConfigs[val.Id].IpSuffix, - - ConsumerMnemonic: tr.TestConfig.ValidatorConfigs[val.Id].ConsumerMnemonic, - ConsumerPrivValidatorKey: tr.TestConfig.ValidatorConfigs[val.Id].ConsumerPrivValidatorKey, - // if true node will be started with consumer key for each consumer chain - StartWithConsumerKey: tr.TestConfig.ValidatorConfigs[val.Id].UseConsumerKey, - }) - } - - vals, err := json.Marshal(validators) - if err != nil { - log.Fatal(err) - } - - // Concat genesis changes defined in chain config, with any custom genesis changes for this chain instantiation - var genesisChanges string - if action.GenesisChanges != "" { - genesisChanges = chainConfig.GenesisChanges + " | " + action.GenesisChanges - } else { - genesisChanges = chainConfig.GenesisChanges - } - - isConsumer := true - changeoverScript := tr.Target.GetTestScriptPath(isConsumer, "start-changeover.sh") - cmd := tr.Target.ExecCommand( - "/bin/bash", - changeoverScript, chainConfig.UpgradeBinary, string(vals), - "sover", chainConfig.IpPrefix, genesisChanges, - tr.TestConfig.TendermintConfigOverride, - ) - - cmdReader, err := cmd.StdoutPipe() - if err != nil { - log.Fatal(err) - } - cmd.Stderr = cmd.Stdout - - if err := cmd.Start(); err != nil { - log.Fatal(err) - } - - scanner := bufio.NewScanner(cmdReader) - - for scanner.Scan() { - out := scanner.Text() - if verbose { - fmt.Println("startChangeover: " + out) - } - if out == done { - break - } - } - if err := scanner.Err(); err != nil { - log.Fatal("startChangeover died", err) - } -} - type AddChainToRelayerAction struct { Chain ChainID Validator ValidatorID diff --git a/tests/interchain/chainsuite/chain.go b/tests/interchain/chainsuite/chain.go index d6f5456563..0df1b97770 100644 --- a/tests/interchain/chainsuite/chain.go +++ b/tests/interchain/chainsuite/chain.go @@ -4,10 +4,13 @@ import ( "context" "encoding/json" "fmt" + "io" + "net/http" "path" "strconv" "strings" "sync" + "time" sdkmath "cosmossdk.io/math" abci "github.com/cometbft/cometbft/abci/types" @@ -16,6 +19,7 @@ import ( "github.com/strangelove-ventures/interchaintest/v8" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/tidwall/gjson" "golang.org/x/sync/errgroup" ) @@ -28,6 +32,8 @@ type Chain struct { ValidatorWallets []ValidatorWallet RelayerWallet ibc.Wallet TestWallets []ibc.Wallet + walletMtx sync.Mutex + walletsInUse map[int]bool } type ValidatorWallet struct { @@ -45,11 +51,12 @@ func chainFromCosmosChain(cosmos *cosmos.CosmosChain, relayerWallet ibc.Wallet, c.ValidatorWallets = wallets c.RelayerWallet = relayerWallet c.TestWallets = testWallets + c.walletsInUse = make(map[int]bool) return c, nil } -// CreateProviderChain creates a single new chain with the given version and returns the chain object. -func CreateProviderChain(ctx context.Context, testName interchaintest.TestName, spec *interchaintest.ChainSpec) (*Chain, error) { +// CreateChain creates a single new chain with the given version and returns the chain object. +func CreateChain(ctx context.Context, testName interchaintest.TestName, spec *interchaintest.ChainSpec) (*Chain, error) { cf := interchaintest.NewBuiltinChainFactory( GetLogger(ctx), []*interchaintest.ChainSpec{spec}, @@ -84,7 +91,7 @@ func CreateProviderChain(ctx context.Context, testName interchaintest.TestName, } // build test wallets - testWallets, err := setupTestWallets(ctx, cosmosChain, TestWalletsNumber) + testWallets, err := SetupTestWallets(ctx, cosmosChain, TestWalletsNumber) if err != nil { return nil, err } @@ -96,7 +103,7 @@ func CreateProviderChain(ctx context.Context, testName interchaintest.TestName, return chain, nil } -func setupTestWallets(ctx context.Context, cosmosChain *cosmos.CosmosChain, walletCount int) ([]ibc.Wallet, error) { +func SetupTestWallets(ctx context.Context, cosmosChain *cosmos.CosmosChain, walletCount int) ([]ibc.Wallet, error) { wallets := make([]ibc.Wallet, walletCount) eg := new(errgroup.Group) for i := 0; i < walletCount; i++ { @@ -155,6 +162,176 @@ func getValidatorWallets(ctx context.Context, chain *Chain) ([]ValidatorWallet, return wallets, nil } +func (p *Chain) AddConsumerChain(ctx context.Context, relayer *Relayer, spec *interchaintest.ChainSpec) (*Chain, error) { + dockerClient, dockerNetwork := GetDockerContext(ctx) + + cf := interchaintest.NewBuiltinChainFactory( + GetLogger(ctx), + []*interchaintest.ChainSpec{spec}, + ) + + chains, err := cf.Chains(p.GetNode().TestName) + if err != nil { + return nil, err + } + consumer := chains[0].(*cosmos.CosmosChain) + + // We can't use AddProviderConsumerLink here because the provider chain is already built; we'll have to do everything by hand. + p.Consumers = append(p.Consumers, consumer) + consumer.Provider = p.CosmosChain + relayerWallet, err := consumer.BuildRelayerWallet(ctx, "relayer-"+consumer.Config().ChainID) + if err != nil { + return nil, err + } + wallets := make([]ibc.Wallet, len(p.Validators)+1) + wallets[0] = relayerWallet + // This is a hack, but we need to create wallets for the validators that have the right moniker. + for i := 1; i <= len(p.Validators); i++ { + wallets[i], err = consumer.BuildRelayerWallet(ctx, ValidatorMoniker) + if err != nil { + return nil, err + } + } + walletAmounts := make([]ibc.WalletAmount, len(wallets)) + for i, wallet := range wallets { + walletAmounts[i] = ibc.WalletAmount{ + Address: wallet.FormattedAddress(), + Denom: consumer.Config().Denom, + Amount: sdkmath.NewInt(TotalValidatorFunds), + } + } + + ic := interchaintest.NewInterchain(). + AddChain(consumer, walletAmounts...). + AddRelayer(relayer, "relayer") + + if err := ic.Build(ctx, GetRelayerExecReporter(ctx), interchaintest.InterchainBuildOptions{ + Client: dockerClient, + NetworkID: dockerNetwork, + TestName: p.GetNode().TestName, + }); err != nil { + return nil, err + } + + for i, val := range consumer.Validators { + if err := val.RecoverKey(ctx, ValidatorMoniker, wallets[i+1].Mnemonic()); err != nil { + return nil, err + } + } + consumerChain, err := chainFromCosmosChain(consumer, relayerWallet, p.TestWallets) + if err != nil { + return nil, err + } + + return consumerChain, nil +} + +// GetUnusedTestingAddresss retrieves an unused wallet address and its key name safely +func (p *Chain) GetUnusedTestingAddresss() (formattedAddress string, keyName string, err error) { + p.walletMtx.Lock() + defer p.walletMtx.Unlock() + + for i, wallet := range p.TestWallets { + if !p.walletsInUse[i] { + p.walletsInUse[i] = true + return wallet.FormattedAddress(), wallet.KeyName(), nil + } + } + + return "", "", fmt.Errorf("no unused wallets available") +} + +// UpdateAndVerifyStakeChange updates the staking amount on the provider chain and verifies that the change is reflected on the consumer side +func (p *Chain) UpdateAndVerifyStakeChange(ctx context.Context, consumer *Chain, relayer *Relayer, amount, valIdx int) error { + + providerAddress := p.ValidatorWallets[valIdx] + + providerHex, err := p.GetValidatorHexAddress(ctx, valIdx) + if err != nil { + return err + } + consumerHex, err := consumer.GetValidatorHexAddress(ctx, valIdx) + if err != nil { + return err + } + + providerPowerBefore, err := p.GetValidatorPower(ctx, providerHex) + if err != nil { + return err + } + + // increase the stake for the given validator + _, err = p.Validators[valIdx].ExecTx(ctx, providerAddress.Moniker, + "staking", "delegate", + providerAddress.ValoperAddress, fmt.Sprintf("%d%s", amount, p.Config().Denom), + ) + if err != nil { + return err + } + + // check that the validator power is updated on both, provider and consumer chains + tCtx, tCancel := context.WithTimeout(ctx, 5*time.Minute) + defer tCancel() + var retErr error + for tCtx.Err() == nil { + retErr = nil + providerPower, err := p.GetValidatorPower(ctx, providerHex) + if err != nil { + return err + } + consumerPower, err := consumer.GetValidatorPower(ctx, consumerHex) + if err != nil { + return err + } + if providerPowerBefore >= providerPower { + retErr = fmt.Errorf("provider power did not increase after delegation") + } else if providerPower != consumerPower { + retErr = fmt.Errorf("consumer power did not update after provider delegation") + } + if retErr == nil { + break + } + time.Sleep(CommitTimeout) + } + return retErr +} + +func (p *Chain) GetValidatorHexAddress(ctx context.Context, valIdx int) (string, error) { + json, err := p.Validators[valIdx].ReadFile(ctx, "config/priv_validator_key.json") + if err != nil { + return "", err + } + return gjson.GetBytes(json, "address").String(), nil +} + +func (c *Chain) GetValidatorPower(ctx context.Context, hexaddr string) (int64, error) { + var power int64 + err := checkEndpoint(c.GetHostRPCAddress()+"/validators", func(b []byte) error { + power = gjson.GetBytes(b, fmt.Sprintf("result.validators.#(address==\"%s\").voting_power", hexaddr)).Int() + if power == 0 { + return fmt.Errorf("validator %s power not found; validators are: %s", hexaddr, string(b)) + } + return nil + }) + if err != nil { + return 0, err + } + return power, nil +} + +func checkEndpoint(url string, f func([]byte) error) error { + resp, err := http.Get(url) //nolint:gosec + if err != nil { + return err + } + defer resp.Body.Close() + bts, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + return f(bts) +} + func (c *Chain) WaitForProposalStatus(ctx context.Context, proposalID string, status govv1.ProposalStatus) error { propID, err := strconv.ParseInt(proposalID, 10, 64) if err != nil { @@ -201,8 +378,8 @@ func (c *Chain) SubmitAndVoteForProposal(ctx context.Context, prop cosmos.TxProp } // builds proposal message, submits, votes and wait for proposal expected status -func (c *Chain) ExecuteProposalMsg(ctx context.Context, proposalMsg cosmos.ProtoMessage, proposer string, chainName string, vote string, expectedStatus govv1.ProposalStatus, expedited bool) error { - proposal, err := c.BuildProposal([]cosmos.ProtoMessage{proposalMsg}, chainName, "summary", "", GovMinDepositString, proposer, false) +func (c *Chain) ExecuteProposalMsg(ctx context.Context, proposalMsg cosmos.ProtoMessage, proposer string, title string, vote string, expectedStatus govv1.ProposalStatus, expedited bool) error { + proposal, err := c.BuildProposal([]cosmos.ProtoMessage{proposalMsg}, title, "summary", "", GovMinDepositString, proposer, false) if err != nil { return err } @@ -439,6 +616,54 @@ func (c *Chain) GetValidatorKey(ctx context.Context, validatorIndex int) (string return address, nil } +func (c *Chain) GetCcvConsumerParams(ctx context.Context) (ConsumerParamsResponse, error) { + queryRes, _, err := c.GetNode().ExecQuery( + ctx, + "ccvconsumer", "params", + ) + if err != nil { + return ConsumerParamsResponse{}, err + } + + var queryResponse ConsumerParamsResponse + err = json.Unmarshal([]byte(queryRes), &queryResponse) + if err != nil { + return ConsumerParamsResponse{}, err + } + + return queryResponse, nil +} + +func (c *Chain) GetProviderInfo(ctx context.Context) (ProviderInfoResponse, error) { + queryRes, _, err := c.GetNode().ExecQuery( + ctx, + "ccvconsumer", "provider-info", + ) + if err != nil { + return ProviderInfoResponse{}, err + } + + var queryResponse ProviderInfoResponse + err = json.Unmarshal([]byte(queryRes), &queryResponse) + if err != nil { + return ProviderInfoResponse{}, err + } + + return queryResponse, nil +} + +func (c *Chain) QueryJSON(ctx context.Context, jsonPath string, query ...string) (gjson.Result, error) { + stdout, _, err := c.GetNode().ExecQuery(ctx, query...) + if err != nil { + return gjson.Result{}, err + } + retval := gjson.GetBytes(stdout, jsonPath) + if !retval.Exists() { + return gjson.Result{}, fmt.Errorf("json path %s not found in query result %s", jsonPath, stdout) + } + return retval, nil +} + func getEvtAttribute(events []abci.Event, evtType string, key string) (string, bool) { for _, evt := range events { if evt.GetType() == evtType { diff --git a/tests/interchain/chainsuite/chain_spec_consumer.go b/tests/interchain/chainsuite/chain_spec_consumer.go new file mode 100644 index 0000000000..2f0a6bc873 --- /dev/null +++ b/tests/interchain/chainsuite/chain_spec_consumer.go @@ -0,0 +1,103 @@ +package chainsuite + +import ( + "context" + "errors" + "strconv" + "time" + + providertypes "github.com/cosmos/interchain-security/v6/x/ccv/provider/types" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/strangelove-ventures/interchaintest/v8/testutil" +) + +func GetConsumerSpec(ctx context.Context, providerChain *Chain, proposalMsg *providertypes.MsgCreateConsumer) *interchaintest.ChainSpec { + fullNodes := FullNodeCount + validators := 1 + + return &interchaintest.ChainSpec{ + ChainName: ConsumerChainID, + NumFullNodes: &fullNodes, + NumValidators: &validators, + ChainConfig: ibc.ChainConfig{ + ChainID: ConsumerChainID, + Bin: ConsumerBin, + Denom: Stake, + Type: CosmosChainType, + GasPrices: GasPrices + Stake, + GasAdjustment: 2.0, + TrustingPeriod: "336h", + CoinType: "118", + Images: []ibc.DockerImage{ + { + Repository: ConsumerImageName(), + Version: ConsumerImageVersion(), + UIDGID: "1025:1025", + }, + }, + ConfigFileOverrides: map[string]any{ + "config/config.toml": DefaultConfigToml(), + }, + PreGenesis: func(consumer ibc.Chain) error { + // note that if Top_N>0 proposal will be rejected. If there is a need to support this in the future, + // it is necessary to first submit a create message with Top_N=0 and then an update message with Top_N>0 + consumerID, err := providerChain.CreateConsumer(ctx, proposalMsg, ValidatorMoniker) + if err != nil { + return err + } + + for index := 0; index < len(providerChain.Validators); index++ { + if err := providerChain.OptIn(ctx, consumerID, index); err != nil { + return err + } + } + + // speed up chain launch by submitting update msg with current time as a spawn time + proposalMsg.InitializationParameters.SpawnTime = time.Now() + updateMsg := &providertypes.MsgUpdateConsumer{ + ConsumerId: consumerID, + Owner: providerChain.ValidatorWallets[0].Address, + NewOwnerAddress: providerChain.ValidatorWallets[0].Address, + InitializationParameters: proposalMsg.InitializationParameters, + PowerShapingParameters: proposalMsg.PowerShapingParameters, + } + if err := providerChain.UpdateConsumer(ctx, updateMsg, ValidatorMoniker); err != nil { + return err + } + + if err := testutil.WaitForBlocks(ctx, 2, providerChain); err != nil { + return err + } + + consumerChain, err := providerChain.GetConsumerChainByChainId(ctx, proposalMsg.ChainId) + if err != nil { + return err + } + + if consumerChain.Phase != providertypes.CONSUMER_PHASE_LAUNCHED.String() { + return errors.New("consumer chain is not launched") + } + + return nil + }, + Bech32Prefix: Bech32PrefixConsumer, + ModifyGenesisAmounts: DefaultGenesisAmounts(Stake), + ModifyGenesis: cosmos.ModifyGenesis(consumerModifiedGenesis()), + InterchainSecurityConfig: ibc.ICSConfig{ + ConsumerCopyProviderKey: func(int) bool { return true }, + }, + }, + } +} + +func consumerModifiedGenesis() []cosmos.GenesisKV { + return []cosmos.GenesisKV{ + cosmos.NewGenesisKV("app_state.slashing.params.signed_blocks_window", strconv.Itoa(SlashingWindowConsumer)), + cosmos.NewGenesisKV("consensus.params.block.max_gas", "50000000"), + cosmos.NewGenesisKV("app_state.ccvconsumer.params.soft_opt_out_threshold", "0.0"), + cosmos.NewGenesisKV("app_state.ccvconsumer.params.blocks_per_distribution_transmission", BlocksPerDistribution), + cosmos.NewGenesisKV("app_state.ccvconsumer.params.reward_denoms", []string{Stake}), + } +} diff --git a/tests/interchain/chainsuite/chain_spec_provider.go b/tests/interchain/chainsuite/chain_spec_provider.go index 9643f1dcd2..b6018e028c 100644 --- a/tests/interchain/chainsuite/chain_spec_provider.go +++ b/tests/interchain/chainsuite/chain_spec_provider.go @@ -1,15 +1,13 @@ package chainsuite import ( - "strconv" - "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v8/ibc" "github.com/strangelove-ventures/interchaintest/v8" ) -func GetProviderSpec(validatorCount int) *interchaintest.ChainSpec { +func GetProviderSpec(validatorCount int, modifiedGenesis []cosmos.GenesisKV) *interchaintest.ChainSpec { fullNodes := FullNodeCount validators := validatorCount @@ -33,25 +31,8 @@ func GetProviderSpec(validatorCount int) *interchaintest.ChainSpec { Repository: ProviderImageName(), UIDGID: "1025:1025", }}, - ModifyGenesis: cosmos.ModifyGenesis(providerModifiedGenesis()), + ModifyGenesis: cosmos.ModifyGenesis(modifiedGenesis), ModifyGenesisAmounts: DefaultGenesisAmounts(Stake), }, } } - -func providerModifiedGenesis() []cosmos.GenesisKV { - return []cosmos.GenesisKV{ - cosmos.NewGenesisKV("app_state.staking.params.unbonding_time", ProviderUnbondingTime.String()), - cosmos.NewGenesisKV("app_state.gov.params.voting_period", GovVotingPeriod.String()), - cosmos.NewGenesisKV("app_state.gov.params.max_deposit_period", GovDepositPeriod.String()), - cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.denom", Stake), - cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.amount", strconv.Itoa(GovMinDepositAmount)), - cosmos.NewGenesisKV("app_state.slashing.params.signed_blocks_window", strconv.Itoa(ProviderSlashingWindow)), - cosmos.NewGenesisKV("app_state.slashing.params.downtime_jail_duration", DowntimeJailDuration.String()), - cosmos.NewGenesisKV("app_state.slashing.params.slash_fraction_double_sign", SlashFractionDoubleSign), - cosmos.NewGenesisKV("app_state.provider.params.slash_meter_replenish_period", ProviderReplenishPeriod), - cosmos.NewGenesisKV("app_state.provider.params.slash_meter_replenish_fraction", ProviderReplenishFraction), - cosmos.NewGenesisKV("app_state.provider.params.blocks_per_epoch", "1"), - cosmos.NewGenesisKV("app_state.staking.params.max_validators", "1"), - } -} diff --git a/tests/interchain/chainsuite/chain_spec_sovereign.go b/tests/interchain/chainsuite/chain_spec_sovereign.go new file mode 100644 index 0000000000..be396f6678 --- /dev/null +++ b/tests/interchain/chainsuite/chain_spec_sovereign.go @@ -0,0 +1,52 @@ +package chainsuite + +import ( + "strconv" + + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" +) + +func GetSovereignSpec() *interchaintest.ChainSpec { + fullNodes := FullNodeCount + validators := 1 + + return &interchaintest.ChainSpec{ + ChainName: SovereignToConsumerChainID, + NumFullNodes: &fullNodes, + NumValidators: &validators, + ChainConfig: ibc.ChainConfig{ + ChainID: SovereignToConsumerChainID, + Bin: SovereignBin, + Denom: Stake, + Type: CosmosChainType, + GasPrices: GasPrices + Stake, + GasAdjustment: 2.0, + TrustingPeriod: "336h", + CoinType: "118", + Images: []ibc.DockerImage{ + { + Repository: SouvereignImageName(), + Version: SouvereignImageVersion(), + UIDGID: "1025:1025", + }, + }, + ConfigFileOverrides: map[string]any{ + "config/config.toml": DefaultConfigToml(), + }, + Bech32Prefix: Bech32PrefixConsumer, + ModifyGenesis: cosmos.ModifyGenesis(sovereignModifiedGenesis()), + ModifyGenesisAmounts: DefaultGenesisAmounts(Stake), + }, + } +} + +func sovereignModifiedGenesis() []cosmos.GenesisKV { + return []cosmos.GenesisKV{ + cosmos.NewGenesisKV("app_state.gov.params.voting_period", GovVotingPeriod.String()), + cosmos.NewGenesisKV("app_state.gov.params.max_deposit_period", GovDepositPeriod.String()), + cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.denom", Stake), + cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.amount", strconv.Itoa(GovMinDepositAmount)), + } +} diff --git a/tests/interchain/chainsuite/config.go b/tests/interchain/chainsuite/config.go index 36b865a55a..ea42399ec7 100644 --- a/tests/interchain/chainsuite/config.go +++ b/tests/interchain/chainsuite/config.go @@ -11,32 +11,37 @@ import ( ) const ( - ProviderBin = "interchain-security-pd" - ProviderBech32Prefix = "cosmos" - ProviderValOperPrefix = "cosmosvaloper" - ProviderChainID = "ics-provider" - Stake = "stake" - DowntimeJailDuration = 10 * time.Second - SlashFractionDoubleSign = "0.05" - ProviderSlashingWindow = 10 - ProviderUnbondingTime = 10 * time.Second - ProviderReplenishPeriod = "2s" - ProviderReplenishFraction = "1.00" - GovMinDepositAmount = 1000 - GovMinDepositString = "1000" + Stake - GovDepositPeriod = 10 * time.Second - GovVotingPeriod = 15 * time.Second - GasPrices = "0.005" - UpgradeDelta = 30 - SlashingWindowConsumer = 20 - CommitTimeout = 2 * time.Second - TotalValidatorFunds = 11_000_000_000 - ValidatorFunds = 30_000_000 - FullNodeCount = 0 - ChainSpawnWait = 155 * time.Second - CosmosChainType = "cosmos" - GovModuleAddress = "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn" - TestWalletsNumber = 20 // Ensure that test accounts are used in a way that maintains the mutual independence of tests + ProviderBin = "interchain-security-pd" + SovereignBin = "interchain-security-sd" + ConsumerBin = "interchain-security-cdd" + ProviderChainID = "ics-provider" + SovereignToConsumerChainID = "ics-sovereign-consumer" + ConsumerChainID = "ics-consumer" + ProviderBech32Prefix = "cosmos" + Bech32PrefixConsumer = "consumer" + Stake = "stake" + DowntimeJailDuration = 10 * time.Second + SlashFractionDoubleSign = "0.05" + ProviderSlashingWindow = 10 + ProviderUnbondingTime = 10 * time.Second + ProviderReplenishPeriod = "2s" + ProviderReplenishFraction = "1.00" + GovMinDepositAmount = 1000 + GovMinDepositString = "1000" + Stake + GovDepositPeriod = 10 * time.Second + GovVotingPeriod = 15 * time.Second + GasPrices = "0.005" + UpgradeDelta = 30 + SlashingWindowConsumer = 20 + CommitTimeout = 2 * time.Second + TotalValidatorFunds = 11_000_000_000 + ValidatorFunds = 30_000_000 + FullNodeCount = 0 + BlocksPerDistribution = 5 + CosmosChainType = "cosmos" + ProviderGovModuleAddress = "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn" + ConsumerGovModuleAddress = "consumer10d07y265gmmuvt4z0w9aw880jnsr700jlh7295" + TestWalletsNumber = 20 ) func DefaultConfigToml() testutil.Toml { @@ -63,19 +68,43 @@ func DefaultGenesisAmounts(denom string) func(i int) (sdktypes.Coin, sdktypes.Co } func ProviderImageVersion() string { - providerImageVersion := os.Getenv("PROVIDER_IMAGE_TAG") - if providerImageVersion == "" { - providerImageVersion = "latest" + return getImageVersion("PROVIDER_IMAGE_TAG") +} + +func ProviderImageName() string { + return getImageName("PROVIDER_IMAGE_NAME") +} + +func SouvereignImageVersion() string { + return getImageVersion("SOVEREIGN_IMAGE_TAG") +} + +func SouvereignImageName() string { + return getImageName("SOVEREIGN_IMAGE_NAME") +} + +func ConsumerImageVersion() string { + return getImageVersion("CONSUMER_IMAGE_TAG") +} + +func ConsumerImageName() string { + return getImageName("CONSUMER_IMAGE_NAME") +} + +func getImageName(imgName string) string { + imageName := os.Getenv(imgName) + if imageName == "" { + imageName = "ghcr.io/cosmos/interchain-security" } - return providerImageVersion + return imageName } -func ProviderImageName() string { - providerImageName := os.Getenv("PROVIDER_IMAGE_NAME") - if providerImageName == "" { - providerImageName = "ghcr.io/cosmos/interchain-security" +func getImageVersion(imgVersion string) string { + imageVersion := os.Getenv(imgVersion) + if imageVersion == "" { + imageVersion = "latest" } - return providerImageName + return imageVersion } diff --git a/tests/interchain/chainsuite/query_types.go b/tests/interchain/chainsuite/query_types.go index 05efddf405..09c98da563 100644 --- a/tests/interchain/chainsuite/query_types.go +++ b/tests/interchain/chainsuite/query_types.go @@ -188,3 +188,36 @@ type ValidatorConsumerAddressResponse struct { type ValidatorProviderAddressResponse struct { ProviderAddress string `json:"provider_address"` } + +type ConsumerParamsResponse struct { + Params Params `json:"params"` +} + +type ConsumerParams struct { + Enabled bool `json:"enabled"` + BlocksPerDistributionTransmission string `json:"blocks_per_distribution_transmission"` + DistributionTransmissionChannel string `json:"distribution_transmission_channel"` + ProviderFeePoolAddrStr string `json:"provider_fee_pool_addr_str"` + CCVTimeoutPeriod string `json:"ccv_timeout_period"` + TransferTimeoutPeriod string `json:"transfer_timeout_period"` + ConsumerRedistributionFraction string `json:"consumer_redistribution_fraction"` + HistoricalEntries string `json:"historical_entries"` + UnbondingPeriod string `json:"unbonding_period"` + SoftOptOutThreshold string `json:"soft_opt_out_threshold"` + RewardDenoms []string `json:"reward_denoms"` + ProviderRewardDenoms []string `json:"provider_reward_denoms"` + RetryDelayPeriod string `json:"retry_delay_period"` + ConsumerID string `json:"consumer_id"` +} + +type ProviderInfoResponse struct { + Consumer ChainDetails `json:"consumer"` + Provider ChainDetails `json:"provider"` +} + +type ChainDetails struct { + ChainID string `json:"chainID"` + ClientID string `json:"clientID"` + ConnectionID string `json:"connectionID"` + ChannelID string `json:"channelID"` +} diff --git a/tests/interchain/chainsuite/relayer.go b/tests/interchain/chainsuite/relayer.go new file mode 100644 index 0000000000..6880f9e423 --- /dev/null +++ b/tests/interchain/chainsuite/relayer.go @@ -0,0 +1,186 @@ +package chainsuite + +import ( + "context" + "encoding/json" + "fmt" + "sort" + "time" + + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/strangelove-ventures/interchaintest/v8/relayer" + "github.com/tidwall/gjson" +) + +type Relayer struct { + ibc.Relayer +} + +func NewRelayer(ctx context.Context, testName interchaintest.TestName) (*Relayer, error) { + dockerClient, dockerNetwork := GetDockerContext(ctx) + rly := interchaintest.NewBuiltinRelayerFactory( + ibc.Hermes, + GetLogger(ctx), + relayer.CustomDockerImage("ghcr.io/informalsystems/hermes", "1.10.3", "2000:2000"), + ).Build(testName, dockerClient, dockerNetwork) + return &Relayer{Relayer: rly}, nil +} + +func (r *Relayer) SetupChainKeys(ctx context.Context, chain *Chain) error { + rep := GetRelayerExecReporter(ctx) + rpcAddr, grpcAddr := chain.GetRPCAddress(), chain.GetGRPCAddress() + if !r.UseDockerNetwork() { + rpcAddr, grpcAddr = chain.GetHostRPCAddress(), chain.GetHostGRPCAddress() + } + + chainName := chain.Config().ChainID + if err := r.AddChainConfiguration(ctx, rep, chain.Config(), chainName, rpcAddr, grpcAddr); err != nil { + return err + } + + return r.RestoreKey(ctx, rep, chain.Config(), chainName, chain.RelayerWallet.Mnemonic()) +} + +func (r *Relayer) GetTransferChannel(ctx context.Context, chain, counterparty *Chain) (*ibc.ChannelOutput, error) { + return r.GetChannelWithPort(ctx, chain, counterparty, "transfer") +} + +func (r *Relayer) GetChannelWithPort(ctx context.Context, chain, counterparty *Chain, portID string) (*ibc.ChannelOutput, error) { + clients, err := r.GetClients(ctx, GetRelayerExecReporter(ctx), chain.Config().ChainID) + if err != nil { + return nil, err + } + var client *ibc.ClientOutput + for _, c := range clients { + if c.ClientState.ChainID == counterparty.Config().ChainID { + client = c + break + } + } + if client == nil { + return nil, fmt.Errorf("no client found for chain %s", counterparty.Config().ChainID) + } + + stdout, _, err := chain.GetNode().ExecQuery(ctx, "ibc", "connection", "connections") + if err != nil { + return nil, fmt.Errorf("error querying connections: %w", err) + } + connections := gjson.GetBytes(stdout, fmt.Sprintf("connections.#(client_id==\"%s\")#.id", client.ClientID)).Array() + if len(connections) == 0 { + return nil, fmt.Errorf("no connections found for client %s", client.ClientID) + } + for _, connID := range connections { + stdout, _, err := chain.GetNode().ExecQuery(ctx, "ibc", "channel", "connections", connID.String()) + if err != nil { + return nil, err + } + channelJSON := gjson.GetBytes(stdout, fmt.Sprintf("channels.#(port_id==\"%s\")", portID)).String() + if channelJSON != "" { + channelOutput := &ibc.ChannelOutput{} + if err := json.Unmarshal([]byte(channelJSON), channelOutput); err != nil { + return nil, fmt.Errorf("error unmarshalling channel output %s: %w", channelJSON, err) + } + return channelOutput, nil + } + } + return nil, fmt.Errorf("no channel found for port %s", portID) +} + +func (r *Relayer) RestartRelayer(ctx context.Context) error { + rep := GetRelayerExecReporter(ctx) + if err := r.StopRelayer(ctx, rep); err != nil { + return err + } + if err := r.StartRelayer(ctx, rep); err != nil { + return err + } + + return nil +} + +func (r *Relayer) ConnectProviderConsumer(ctx context.Context, provider *Chain, consumer *Chain) error { + icsPath := relayerICSPathFor(provider, consumer) + rep := GetRelayerExecReporter(ctx) + if err := r.GeneratePath(ctx, rep, consumer.Config().ChainID, provider.Config().ChainID, icsPath); err != nil { + return err + } + + consumerClients, err := r.GetClients(ctx, rep, consumer.Config().ChainID) + if err != nil { + return err + } + sort.Slice(consumerClients, func(i, j int) bool { + return consumerClients[i].ClientID > consumerClients[j].ClientID + }) + var consumerClient *ibc.ClientOutput + for _, client := range consumerClients { + if client.ClientState.ChainID == provider.Config().ChainID { + consumerClient = client + break + } + } + if consumerClient == nil { + return fmt.Errorf("consumer chain %s does not have a client tracking the provider chain %s", consumer.Config().ChainID, provider.Config().ChainID) + } + consumerClientID := consumerClient.ClientID + + providerClients, err := r.GetClients(ctx, rep, provider.Config().ChainID) + if err != nil { + return err + } + sort.Slice(providerClients, func(i, j int) bool { + return providerClients[i].ClientID > providerClients[j].ClientID + }) + + var providerClient *ibc.ClientOutput + for _, client := range providerClients { + if client.ClientState.ChainID == consumer.Config().ChainID { + providerClient = client + break + } + } + if providerClient == nil { + return fmt.Errorf("provider chain %s does not have a client tracking the consumer chain %s for path %s on relayer %s", + provider.Config().ChainID, consumer.Config().ChainID, icsPath, r) + } + providerClientID := providerClient.ClientID + + if err := r.UpdatePath(ctx, rep, icsPath, ibc.PathUpdateOptions{ + SrcClientID: &consumerClientID, + DstClientID: &providerClientID, + }); err != nil { + return err + } + + if err := r.CreateConnections(ctx, rep, icsPath); err != nil { + return err + } + + if err := r.CreateChannel(ctx, rep, icsPath, ibc.CreateChannelOptions{ + SourcePortName: "consumer", + DestPortName: "provider", + Order: ibc.Ordered, + Version: "1", + }); err != nil { + return err + } + + tCtx, tCancel := context.WithTimeout(ctx, 30*CommitTimeout) + defer tCancel() + for tCtx.Err() == nil { + var ch *ibc.ChannelOutput + ch, err = r.GetTransferChannel(ctx, provider, consumer) + if err == nil && ch != nil { + break + } else if err == nil { + err = fmt.Errorf("channel not found") + } + time.Sleep(CommitTimeout) + } + return err +} + +func relayerICSPathFor(chainA, chainB *Chain) string { + return fmt.Sprintf("ics-%s-%s", chainA.Config().ChainID, chainB.Config().ChainID) +} diff --git a/tests/interchain/go.mod b/tests/interchain/go.mod index 8215ada4a2..623605d666 100644 --- a/tests/interchain/go.mod +++ b/tests/interchain/go.mod @@ -6,6 +6,7 @@ toolchain go1.22.10 require ( cosmossdk.io/math v1.4.0 + cosmossdk.io/x/upgrade v0.1.4 github.com/cometbft/cometbft v0.38.15 github.com/cosmos/cosmos-sdk v0.50.10 github.com/cosmos/ibc-go/v8 v8.5.2 @@ -13,8 +14,10 @@ require ( github.com/docker/docker v25.0.6+incompatible github.com/strangelove-ventures/interchaintest/v8 v8.7.1 github.com/stretchr/testify v1.9.0 + github.com/tidwall/gjson v1.18.0 + github.com/tidwall/sjson v1.2.5 go.uber.org/zap v1.27.0 - golang.org/x/sync v0.9.0 + golang.org/x/sync v0.10.0 ) require ( @@ -34,7 +37,6 @@ require ( cosmossdk.io/x/evidence v0.1.1 // indirect cosmossdk.io/x/feegrant v0.1.1 // indirect cosmossdk.io/x/tx v0.13.5 // indirect - cosmossdk.io/x/upgrade v0.1.4 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.2 // indirect @@ -226,7 +228,6 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect github.com/tidwall/btree v1.7.0 // indirect - github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect @@ -248,14 +249,14 @@ require ( go.opentelemetry.io/otel/trace v1.32.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect - golang.org/x/crypto v0.28.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect golang.org/x/mod v0.22.0 // indirect golang.org/x/net v0.30.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/term v0.25.0 // indirect - golang.org/x/text v0.20.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.23.0 // indirect google.golang.org/api v0.186.0 // indirect @@ -290,7 +291,7 @@ replace ( github.com/cosmos/interchain-security/v6 => github.com/cosmos/interchain-security/v6 v6.0.0-20241203112553-01f9698b4450 github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 // Remove this when strangelove-ventures updates the interchain tests to import ICS v6 - github.com/strangelove-ventures/interchaintest/v8 => github.com/stana-miric/interchaintest/v8 v8.0.0-20241022073631-60f2480aacd4 + github.com/strangelove-ventures/interchaintest/v8 => github.com/stana-miric/interchaintest/v8 v8.0.0-20241213083053-58af9adbcc58 github.com/vedhavyas/go-subkey => github.com/strangelove-ventures/go-subkey v1.0.7 ) diff --git a/tests/interchain/go.sum b/tests/interchain/go.sum index 0fc000ff06..be7759bee3 100644 --- a/tests/interchain/go.sum +++ b/tests/interchain/go.sum @@ -1133,8 +1133,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= -github.com/stana-miric/interchaintest/v8 v8.0.0-20241022073631-60f2480aacd4 h1:D7TeNWCVargpSbh/+sG0lNIxTa0XTRoZkNgwTY5riPw= -github.com/stana-miric/interchaintest/v8 v8.0.0-20241022073631-60f2480aacd4/go.mod h1:OOAl+5e4or9FdILfKprkqeVt/WjZt0qBv7bp5fKbBSc= +github.com/stana-miric/interchaintest/v8 v8.0.0-20241213083053-58af9adbcc58 h1:8VyNziFamlVhuhcLpp+6BoR4OwHS1BsRADno3IHfrKo= +github.com/stana-miric/interchaintest/v8 v8.0.0-20241213083053-58af9adbcc58/go.mod h1:OOAl+5e4or9FdILfKprkqeVt/WjZt0qBv7bp5fKbBSc= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -1170,12 +1170,15 @@ 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.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/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= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= @@ -1282,8 +1285,8 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1427,8 +1430,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1527,13 +1530,13 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1544,8 +1547,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/tests/interchain/provider_consumers_suite.go b/tests/interchain/provider_consumers_suite.go new file mode 100644 index 0000000000..f5ccb0370d --- /dev/null +++ b/tests/interchain/provider_consumers_suite.go @@ -0,0 +1,98 @@ +package interchain + +import ( + "context" + "cosmos/interchain-security/tests/interchain/chainsuite" + "strconv" + "time" + + sdkmath "cosmossdk.io/math" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/stretchr/testify/suite" +) + +type ProviderConsumersSuite struct { + suite.Suite + Provider *chainsuite.Chain + Sovereign *chainsuite.Chain + Consumer *chainsuite.Chain + Relayer *chainsuite.Relayer + ctx context.Context +} + +func (s *ProviderConsumersSuite) SetupSuite() { + ctx, err := chainsuite.NewSuiteContext(&s.Suite) + s.Require().NoError(err) + s.ctx = ctx + + // create and start provider chain + s.Provider, err = chainsuite.CreateChain(s.GetContext(), s.T(), chainsuite.GetProviderSpec(1, provConsumerModifiedGenesis())) + s.Require().NoError(err) + + // create and start sovereign chain that will later changeover to consumer + s.Sovereign, err = chainsuite.CreateChain(s.GetContext(), s.T(), chainsuite.GetSovereignSpec()) + s.Require().NoError(err) + + // setup hermes relayer + relayer, err := chainsuite.NewRelayer(s.GetContext(), s.T()) + s.Require().NoError(err) + s.Relayer = relayer + + // create and start consumer chain + spawnTime := time.Now().Add(time.Hour) + initParams := consumerInitParamsTemplate(&spawnTime) + initParams.InitialHeight = clienttypes.Height{RevisionNumber: clienttypes.ParseChainID(chainsuite.ConsumerChainID), RevisionHeight: 1} + proposalMsg := msgCreateConsumer(chainsuite.ConsumerChainID, initParams, powerShapingParamsTemplate(), nil, chainsuite.ProviderGovModuleAddress) + s.Consumer, err = s.Provider.AddConsumerChain(s.GetContext(), relayer, chainsuite.GetConsumerSpec(s.GetContext(), s.Provider, proposalMsg)) + s.Require().NoError(err) + + s.Require().NoError(relayer.SetupChainKeys(s.GetContext(), s.Provider)) + s.Require().NoError(relayer.SetupChainKeys(s.GetContext(), s.Sovereign)) + s.Require().NoError(relayer.SetupChainKeys(s.GetContext(), s.Consumer)) + s.Require().NoError(relayer.RestartRelayer(s.GetContext())) + + // confirm that tx on consumer can not be send before consumer and provider are connected + err = s.Consumer.SendFunds(ctx, chainsuite.ValidatorMoniker, ibc.WalletAmount{ + Amount: sdkmath.NewInt(1000), + Denom: s.Consumer.Config().Denom, + Address: s.Consumer.RelayerWallet.FormattedAddress(), + }) + s.Require().Error(err) + s.Require().Contains(err.Error(), "tx contains unsupported message types") + // connect consumer and provider + s.Require().NoError(s.Relayer.ConnectProviderConsumer(s.GetContext(), s.Provider, s.Consumer)) + s.Require().NoError(relayer.RestartRelayer(s.GetContext())) + s.Require().NoError(s.Provider.UpdateAndVerifyStakeChange(s.GetContext(), s.Consumer, s.Relayer, 1_000_000, 0)) + providerInfo, err := s.Consumer.GetProviderInfo(s.GetContext()) + s.Require().NoError(err) + s.Require().Equal("connection-0", providerInfo.Provider.ConnectionID) + + // build test wallets for consumer after ics connection is established and bank send txs are allowed + testWallets, err := chainsuite.SetupTestWallets(ctx, s.Consumer.CosmosChain, chainsuite.TestWalletsNumber) + s.Require().NoError(err) + s.Consumer.TestWallets = testWallets +} + +func (s *ProviderConsumersSuite) GetContext() context.Context { + s.Require().NotNil(s.ctx, "Tried to GetContext before it was set. SetupSuite must run first") + return s.ctx +} + +func provConsumerModifiedGenesis() []cosmos.GenesisKV { + return []cosmos.GenesisKV{ + cosmos.NewGenesisKV("app_state.staking.params.unbonding_time", (chainsuite.ProviderUnbondingTime * 10000000).String()), + cosmos.NewGenesisKV("app_state.gov.params.voting_period", chainsuite.GovVotingPeriod.String()), + cosmos.NewGenesisKV("app_state.gov.params.max_deposit_period", chainsuite.GovDepositPeriod.String()), + cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.denom", chainsuite.Stake), + cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.amount", strconv.Itoa(chainsuite.GovMinDepositAmount)), + cosmos.NewGenesisKV("app_state.slashing.params.signed_blocks_window", strconv.Itoa(chainsuite.ProviderSlashingWindow)), + cosmos.NewGenesisKV("app_state.slashing.params.downtime_jail_duration", chainsuite.DowntimeJailDuration.String()), + cosmos.NewGenesisKV("app_state.slashing.params.slash_fraction_double_sign", chainsuite.SlashFractionDoubleSign), + cosmos.NewGenesisKV("app_state.provider.params.slash_meter_replenish_period", chainsuite.ProviderReplenishPeriod), + cosmos.NewGenesisKV("app_state.provider.params.slash_meter_replenish_fraction", chainsuite.ProviderReplenishFraction), + cosmos.NewGenesisKV("app_state.provider.params.blocks_per_epoch", "1"), + cosmos.NewGenesisKV("app_state.staking.params.max_validators", "1"), + } +} diff --git a/tests/interchain/provider_consumers_test.go b/tests/interchain/provider_consumers_test.go new file mode 100644 index 0000000000..c8355dd126 --- /dev/null +++ b/tests/interchain/provider_consumers_test.go @@ -0,0 +1,159 @@ +package interchain + +import ( + "context" + "cosmos/interchain-security/tests/interchain/chainsuite" + "fmt" + "testing" + "time" + + sdkmath "cosmossdk.io/math" + upgradetypes "cosmossdk.io/x/upgrade/types" + + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + + transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + providertypes "github.com/cosmos/interchain-security/v6/x/ccv/provider/types" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/strangelove-ventures/interchaintest/v8/testutil" + "github.com/stretchr/testify/suite" + "github.com/tidwall/sjson" + "golang.org/x/sync/errgroup" +) + +func TestProviderConsumersSuite(t *testing.T) { + s := &ProviderConsumersSuite{} + + suite.Run(t, s) +} + +func (s *ProviderConsumersSuite) TestSovereignToConsumerChangeover() { + // submit MsgCreateConsumer and verify that the chain is in launched phase + currentHeight, err := s.Sovereign.Height(s.GetContext()) + s.Require().NoError(err) + spawnTime := time.Now().Add(time.Hour) + initializationParams := consumerInitParamsTemplate(&spawnTime) + initialHeight := uint64(currentHeight) + 60 + initializationParams.InitialHeight = clienttypes.Height{ + RevisionNumber: clienttypes.ParseChainID(s.Sovereign.Config().ChainID), + RevisionHeight: initialHeight, + } + powerShapingParams := powerShapingParamsTemplate() + createConsumerMsg := msgCreateConsumer(s.Sovereign.Config().ChainID, initializationParams, powerShapingParams, nil, s.Provider.ValidatorWallets[0].Address) + consumerID, err := s.Provider.CreateConsumer(s.GetContext(), createConsumerMsg, chainsuite.ValidatorMoniker) + s.Require().NoError(err) + // opt in validator + s.Require().NoError(s.Provider.OptIn(s.GetContext(), consumerID, 0)) + // assign consumer key + valConsumerKey, err := s.Sovereign.GetValidatorKey(s.GetContext(), 0) + s.Require().NoError(err) + s.Require().NoError(s.Provider.AssignKey(s.GetContext(), consumerID, 0, valConsumerKey)) + // update spawn time + initializationParams.SpawnTime = time.Now() + updateMsg := &providertypes.MsgUpdateConsumer{ + Owner: s.Provider.ValidatorWallets[0].Address, + ConsumerId: consumerID, + NewOwnerAddress: s.Provider.ValidatorWallets[0].Address, + InitializationParameters: initializationParams, + PowerShapingParameters: powerShapingParams, + } + s.Require().NoError(s.Provider.UpdateConsumer(s.GetContext(), updateMsg, s.Provider.ValidatorWallets[0].Address)) + s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 2, s.Provider)) + consumerChain, err := s.Provider.GetConsumerChain(s.GetContext(), consumerID) + s.Require().NoError(err) + s.Require().Equal(providertypes.CONSUMER_PHASE_LAUNCHED.String(), consumerChain.Phase) + + // submit sw upgrade proposal + upgradeMsg := &upgradetypes.MsgSoftwareUpgrade{ + Authority: chainsuite.ConsumerGovModuleAddress, + Plan: upgradetypes.Plan{ + Name: "sovereign-changeover", + Height: int64(initialHeight) - 3, + }, + } + s.Require().NoError(s.Sovereign.ExecuteProposalMsg(s.GetContext(), upgradeMsg, chainsuite.ConsumerGovModuleAddress, "Changeover", cosmos.ProposalVoteYes, govv1.StatusPassed, false)) + + // wait for sw upgrade height + currentHeight, err = s.Sovereign.Height(s.GetContext()) + s.Require().NoError(err) + timeoutCtx, timeoutCtxCancel := context.WithTimeout(s.GetContext(), (time.Duration(int64(initialHeight)-currentHeight)+10)*chainsuite.CommitTimeout) + defer timeoutCtxCancel() + err = testutil.WaitForBlocks(timeoutCtx, int(int64(initialHeight)-currentHeight)+3, s.Sovereign) + s.Require().Error(err) + + // stop sovereign chain + s.Require().NoError(s.Sovereign.StopAllNodes(s.GetContext())) + + genesis, err := s.Sovereign.GetNode().GenesisFileContent(s.GetContext()) + s.Require().NoError(err) + + // insert consumer genesis section + ccvState, _, err := s.Provider.GetNode().ExecQuery(s.GetContext(), "provider", "consumer-genesis", consumerID) + s.Require().NoError(err) + genesis, err = sjson.SetRawBytes(genesis, "app_state.ccvconsumer", ccvState) + s.Require().NoError(err) + + genesis, err = sjson.SetBytes(genesis, "app_state.ccvconsumer.preCCV", true) + s.Require().NoError(err) + + eg := errgroup.Group{} + for _, val := range s.Sovereign.Validators { + val := val + eg.Go(func() error { + if err := val.OverwriteGenesisFile(s.GetContext(), []byte(genesis)); err != nil { + return err + } + return val.WriteFile(s.GetContext(), []byte(genesis), ".sovereign/config/genesis.json") + }) + } + s.Require().NoError(eg.Wait()) + + // replace the binary and restart consumer node + s.Sovereign.ChangeBinary(s.GetContext(), chainsuite.ConsumerBin) + s.Require().NoError(s.Sovereign.StartAllNodes(s.GetContext())) + s.Require().NoError(s.Relayer.ConnectProviderConsumer(s.GetContext(), s.Provider, s.Sovereign)) + s.Require().NoError(s.Relayer.StopRelayer(s.GetContext(), chainsuite.GetRelayerExecReporter(s.GetContext()))) + s.Require().NoError(s.Relayer.StartRelayer(s.GetContext(), chainsuite.GetRelayerExecReporter(s.GetContext()))) + params, err := s.Sovereign.GetCcvConsumerParams(s.ctx) + s.Require().NoError(err) + s.Require().True(params.Params.HistoricalEntries == fmt.Sprint(initializationParams.HistoricalEntries)) + // check if consumer is connected and functional + s.Require().NoError(s.Provider.UpdateAndVerifyStakeChange(s.GetContext(), s.Sovereign, s.Relayer, 1_000_000, 0)) +} + +func (s *ProviderConsumersSuite) TestRewards() { + transferCh, err := s.Relayer.GetTransferChannel(s.GetContext(), s.Provider, s.Consumer) + s.Require().NoError(err) + s.Require().True(transferCh != nil) + rewardDenom := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", transferCh.ChannelID, s.Consumer.Config().Denom)).IBCDenom() + + govAuthority, err := s.Provider.GetGovernanceAddress(s.GetContext()) + s.Require().NoError(err) + rewardDenomsProp := &providertypes.MsgChangeRewardDenoms{ + DenomsToAdd: []string{rewardDenom}, + Authority: govAuthority, + } + s.Require().NoError(s.Provider.ExecuteProposalMsg(s.GetContext(), rewardDenomsProp, chainsuite.ProviderGovModuleAddress, "change reward denoms", cosmos.ProposalVoteYes, govv1.StatusPassed, false)) + + s.Require().NoError(s.Consumer.SendFunds(s.GetContext(), interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ + Amount: sdkmath.NewInt(10000), + Denom: s.Consumer.Config().Denom, + Address: s.Consumer.ValidatorWallets[0].Address, + })) + + s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), chainsuite.BlocksPerDistribution+2, s.Provider, s.Consumer)) + s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 2, s.Provider, s.Consumer)) + + rewardStr, err := s.Provider.QueryJSON( + s.GetContext(), fmt.Sprintf("total.#(%%\"*%s\")", rewardDenom), + "distribution", "rewards", s.Provider.ValidatorWallets[0].Address, + ) + s.Require().NoError(err) + rewards, err := StrToSDKInt(rewardStr.String()) + s.Require().NoError(err) + s.Require().True(rewards.GT(sdkmath.NewInt(0)), "rewards: %s", rewards.String()) +} diff --git a/tests/interchain/provider_single_val_test.go b/tests/interchain/provider_single_val_test.go index 2996005efc..053796d624 100644 --- a/tests/interchain/provider_single_val_test.go +++ b/tests/interchain/provider_single_val_test.go @@ -35,7 +35,7 @@ func TestSingleProviderSuite(t *testing.T) { // Confirm that a chain can be created with initialization parameters that do not contain a spawn time // Confirm that if there are no opted-in validators at spawn time, the chain fails to launch and moves back to its Registered phase having reset its spawn time func (s *SingleValidatorProviderSuite) TestProviderCreateConsumer() { - testAcc, testAccKey, err := s.GetUnusedTestingAddresss() + testAcc, testAccKey, err := s.Provider.GetUnusedTestingAddresss() s.Require().NoError(err) // Confirm that a chain can be created with the minimum params (metadata) @@ -89,7 +89,7 @@ func (s *SingleValidatorProviderSuite) TestProviderCreateConsumer() { // Confirm that a chain without the minimum params (metadata) is rejected // Confirm that a chain voted 'no' is rejected func (s *SingleValidatorProviderSuite) TestProviderCreateConsumerRejection() { - testAcc, testAccKey, err := s.GetUnusedTestingAddresss() + testAcc, testAccKey, err := s.Provider.GetUnusedTestingAddresss() s.Require().NoError(err) chainName := "rejectConsumer-1" @@ -111,7 +111,7 @@ func (s *SingleValidatorProviderSuite) TestProviderCreateConsumerRejection() { // Scenario 1: Validators opted in, MsgUpdateConsumer called to set spawn time in the past -> chain should start. // Scenario 2: Validators opted in, spawn time is in the future, the chain starts after the spawn time. func (s *SingleValidatorProviderSuite) TestProviderValidatorOptIn() { - testAcc, testAccKey, err := s.GetUnusedTestingAddresss() + testAcc, testAccKey, err := s.Provider.GetUnusedTestingAddresss() s.Require().NoError(err) // Scenario 1: Validators opted in, MsgUpdateConsumer called to set spawn time in the past -> chain should start. @@ -165,7 +165,7 @@ func (s *SingleValidatorProviderSuite) TestProviderValidatorOptIn() { // -> Check that consumer chain genesis is available and contains the correct validator key // If possible, confirm that a validator can change their key assignment (from hub key to consumer chain key and/or vice versa) func (s *SingleValidatorProviderSuite) TestProviderValidatorOptInWithKeyAssignment() { - testAcc, testAccKey, err := s.GetUnusedTestingAddresss() + testAcc, testAccKey, err := s.Provider.GetUnusedTestingAddresss() s.Require().NoError(err) valConsumerKeyVal := "Ui5Gf1+mtWUdH8u3xlmzdKID+F3PK0sfXZ73GZ6q6is=" @@ -232,7 +232,7 @@ func (s *SingleValidatorProviderSuite) TestProviderValidatorOptInWithKeyAssignme // If there are no opted-in validators and the spawn time is in the past, the chain should not start. // Confirm that a chain remains in the Registered phase unless all the initialization parameters are set for it func (s *SingleValidatorProviderSuite) TestProviderUpdateConsumer() { - testAcc, testAccKey, err := s.GetUnusedTestingAddresss() + testAcc, testAccKey, err := s.Provider.GetUnusedTestingAddresss() s.Require().NoError(err) chainName := "updateConsumer-1" @@ -294,7 +294,7 @@ func (s *SingleValidatorProviderSuite) TestProviderUpdateConsumer() { // Confirm that the chain can be updated to a higher TopN // Confirm that the owner of the chain cannot change as long as it remains a Top N chain func (s *SingleValidatorProviderSuite) TestProviderTransformOptInToTopN() { - testAcc, testAccKey, err := s.GetUnusedTestingAddresss() + testAcc, testAccKey, err := s.Provider.GetUnusedTestingAddresss() s.Require().NoError(err) // Create an opt-in chain, owner is testAcc1 @@ -316,24 +316,24 @@ func (s *SingleValidatorProviderSuite) TestProviderTransformOptInToTopN() { upgradeMsg := &providertypes.MsgUpdateConsumer{ Owner: testAcc, ConsumerId: consumerId, - NewOwnerAddress: chainsuite.GovModuleAddress, + NewOwnerAddress: chainsuite.ProviderGovModuleAddress, } s.Require().NoError(s.Provider.UpdateConsumer(s.GetContext(), upgradeMsg, testAccKey)) consumerChain, err = s.Provider.GetConsumerChain(s.GetContext(), consumerId) s.Require().NoError(err) - s.Require().Equal(chainsuite.GovModuleAddress, consumerChain.OwnerAddress) + s.Require().Equal(chainsuite.ProviderGovModuleAddress, consumerChain.OwnerAddress) // Confirm that the chain can be updated to a lower TopN spawTimeFromNow := 10 * time.Second initParams.SpawnTime = time.Now().Add(spawTimeFromNow) powerShapingParams.Top_N = 50 upgradeMsg = &providertypes.MsgUpdateConsumer{ - Owner: chainsuite.GovModuleAddress, + Owner: chainsuite.ProviderGovModuleAddress, ConsumerId: consumerId, - NewOwnerAddress: chainsuite.GovModuleAddress, + NewOwnerAddress: chainsuite.ProviderGovModuleAddress, InitializationParameters: initParams, PowerShapingParameters: powerShapingParams, } - s.Require().NoError(s.Provider.ExecuteProposalMsg(s.GetContext(), upgradeMsg, chainsuite.GovModuleAddress, chainName, cosmos.ProposalVoteYes, govv1.StatusPassed, false)) + s.Require().NoError(s.Provider.ExecuteProposalMsg(s.GetContext(), upgradeMsg, chainsuite.ProviderGovModuleAddress, chainName, cosmos.ProposalVoteYes, govv1.StatusPassed, false)) time.Sleep(spawTimeFromNow) s.Require().NoError(testutil.WaitForBlocks(s.GetContext(), 1, s.Provider)) updatedChain, err := s.Provider.GetConsumerChain(s.GetContext(), consumerId) @@ -344,12 +344,12 @@ func (s *SingleValidatorProviderSuite) TestProviderTransformOptInToTopN() { //Confirm that the chain can be updated to a higher TopN powerShapingParams.Top_N = 100 upgradeMsg = &providertypes.MsgUpdateConsumer{ - Owner: chainsuite.GovModuleAddress, + Owner: chainsuite.ProviderGovModuleAddress, ConsumerId: consumerId, - NewOwnerAddress: chainsuite.GovModuleAddress, + NewOwnerAddress: chainsuite.ProviderGovModuleAddress, PowerShapingParameters: powerShapingParams, } - s.Require().NoError(s.Provider.ExecuteProposalMsg(s.GetContext(), upgradeMsg, chainsuite.GovModuleAddress, chainName, cosmos.ProposalVoteYes, govv1.StatusPassed, false)) + s.Require().NoError(s.Provider.ExecuteProposalMsg(s.GetContext(), upgradeMsg, chainsuite.ProviderGovModuleAddress, chainName, cosmos.ProposalVoteYes, govv1.StatusPassed, false)) updatedChain, err = s.Provider.GetConsumerChain(s.GetContext(), consumerId) s.Require().NoError(err) s.Require().Equal(providertypes.CONSUMER_PHASE_LAUNCHED.String(), updatedChain.Phase) @@ -357,17 +357,17 @@ func (s *SingleValidatorProviderSuite) TestProviderTransformOptInToTopN() { // Confirm that the owner of the chain cannot change as long as it remains a Top N chain upgradeMsg = &providertypes.MsgUpdateConsumer{ - Owner: chainsuite.GovModuleAddress, + Owner: chainsuite.ProviderGovModuleAddress, ConsumerId: consumerId, NewOwnerAddress: testAcc, } - s.Require().Error(s.Provider.ExecuteProposalMsg(s.GetContext(), upgradeMsg, chainsuite.GovModuleAddress, chainName, cosmos.ProposalVoteYes, govv1.StatusPassed, false)) + s.Require().Error(s.Provider.ExecuteProposalMsg(s.GetContext(), upgradeMsg, chainsuite.ProviderGovModuleAddress, chainName, cosmos.ProposalVoteYes, govv1.StatusPassed, false)) } // Create a Top N chain, and transform it to an opt-in via `tx gov submit-proposal` using MsgUpdateConsumer // Confirm that the chain is now not owned by governance func (s *SingleValidatorProviderSuite) TestProviderTransformTopNtoOptIn() { - testAcc, _, err := s.GetUnusedTestingAddresss() + testAcc, _, err := s.Provider.GetUnusedTestingAddresss() s.Require().NoError(err) chainName := "transformTopNtoOptIn-1" @@ -375,20 +375,20 @@ func (s *SingleValidatorProviderSuite) TestProviderTransformTopNtoOptIn() { spawnTimeFromNow := time.Now().Add(time.Hour) powerShapingParams := powerShapingParamsTemplate() initParams := consumerInitParamsTemplate(&spawnTimeFromNow) - proposalMsg := msgCreateConsumer(chainName, initParams, powerShapingParams, nil, chainsuite.GovModuleAddress) - s.Require().NoError(s.Provider.ExecuteProposalMsg(s.GetContext(), proposalMsg, chainsuite.GovModuleAddress, chainName, cosmos.ProposalVoteYes, govv1.StatusPassed, false)) + proposalMsg := msgCreateConsumer(chainName, initParams, powerShapingParams, nil, chainsuite.ProviderGovModuleAddress) + s.Require().NoError(s.Provider.ExecuteProposalMsg(s.GetContext(), proposalMsg, chainsuite.ProviderGovModuleAddress, chainName, cosmos.ProposalVoteYes, govv1.StatusPassed, false)) consumerChain, err := s.Provider.GetConsumerChainByChainId(s.GetContext(), chainName) s.Require().NoError(err) powerShapingParams.Top_N = 100 initParams.SpawnTime = time.Now().Add(-time.Hour) upgradeMsg := &providertypes.MsgUpdateConsumer{ - Owner: chainsuite.GovModuleAddress, + Owner: chainsuite.ProviderGovModuleAddress, ConsumerId: consumerChain.ConsumerID, - NewOwnerAddress: chainsuite.GovModuleAddress, + NewOwnerAddress: chainsuite.ProviderGovModuleAddress, PowerShapingParameters: powerShapingParams, InitializationParameters: initParams, } - s.Require().NoError(s.Provider.ExecuteProposalMsg(s.GetContext(), upgradeMsg, chainsuite.GovModuleAddress, chainName, cosmos.ProposalVoteYes, govv1.StatusPassed, false)) + s.Require().NoError(s.Provider.ExecuteProposalMsg(s.GetContext(), upgradeMsg, chainsuite.ProviderGovModuleAddress, chainName, cosmos.ProposalVoteYes, govv1.StatusPassed, false)) consumerChain, err = s.Provider.GetConsumerChainByChainId(s.GetContext(), chainName) s.Require().NoError(err) s.Require().Equal(providertypes.CONSUMER_PHASE_LAUNCHED.String(), consumerChain.Phase) @@ -397,12 +397,12 @@ func (s *SingleValidatorProviderSuite) TestProviderTransformTopNtoOptIn() { // Transform to opt in chain powerShapingParams.Top_N = 0 upgradeMsg = &providertypes.MsgUpdateConsumer{ - Owner: chainsuite.GovModuleAddress, + Owner: chainsuite.ProviderGovModuleAddress, ConsumerId: consumerChain.ConsumerID, NewOwnerAddress: testAcc, PowerShapingParameters: powerShapingParams, } - s.Require().NoError(s.Provider.ExecuteProposalMsg(s.GetContext(), upgradeMsg, chainsuite.GovModuleAddress, chainName, cosmos.ProposalVoteYes, govv1.StatusPassed, false)) + s.Require().NoError(s.Provider.ExecuteProposalMsg(s.GetContext(), upgradeMsg, chainsuite.ProviderGovModuleAddress, chainName, cosmos.ProposalVoteYes, govv1.StatusPassed, false)) optInChain, err := s.Provider.GetConsumerChain(s.GetContext(), consumerChain.ConsumerID) s.Require().NoError(err) s.Require().Equal(powerShapingParams.Top_N, uint32(optInChain.PowerShapingParams.TopN)) @@ -411,7 +411,7 @@ func (s *SingleValidatorProviderSuite) TestProviderTransformTopNtoOptIn() { // TestOptOut tests removing validator from consumer-opted-in-validators func (s *SingleValidatorProviderSuite) TestOptOut() { - testAcc, testAccKey, err := s.GetUnusedTestingAddresss() + testAcc, testAccKey, err := s.Provider.GetUnusedTestingAddresss() s.Require().NoError(err) // Add consumer chain @@ -460,7 +460,7 @@ func (s *SingleValidatorProviderSuite) TestOptOut() { // Confirm that after unbonding period, the chain moves to the Deleted phase and things like consumer id to client id // associations are deleted, but the chain metadata and the chain id are not deleted func (s *SingleValidatorProviderSuite) TestProviderRemoveConsumer() { - testAcc, testAccKey, err := s.GetUnusedTestingAddresss() + testAcc, testAccKey, err := s.Provider.GetUnusedTestingAddresss() s.Require().NoError(err) // Test removing a chain @@ -516,9 +516,9 @@ func (s *SingleValidatorProviderSuite) TestProviderRemoveConsumer() { // Confirm that only the owner can send MsgUpdateConsumer, MsgRemoveConsumer // Confirm that ownership can be transferred to a different address -> results in the "old" owner losing ownership func (s *SingleValidatorProviderSuite) TestProviderOwnerChecks() { - testAcc1, testAccKey1, err := s.GetUnusedTestingAddresss() + testAcc1, testAccKey1, err := s.Provider.GetUnusedTestingAddresss() s.Require().NoError(err) - testAcc2, testAccKey2, err := s.GetUnusedTestingAddresss() + testAcc2, testAccKey2, err := s.Provider.GetUnusedTestingAddresss() s.Require().NoError(err) // Create an opt-in chain chainName := "providerOwnerChecks-1" @@ -594,10 +594,10 @@ func (s *SingleValidatorProviderSuite) TestProviderOwnerChecks() { // update owner using proposal is not possible - current owner is among expected signers upgradeMsg = &providertypes.MsgUpdateConsumer{ Owner: testAcc2, - NewOwnerAddress: chainsuite.GovModuleAddress, + NewOwnerAddress: chainsuite.ProviderGovModuleAddress, ConsumerId: consumerId, } - err = s.Provider.ExecuteProposalMsg(s.GetContext(), upgradeMsg, chainsuite.GovModuleAddress, chainName, cosmos.ProposalVoteYes, govv1.StatusPassed, false) + err = s.Provider.ExecuteProposalMsg(s.GetContext(), upgradeMsg, chainsuite.ProviderGovModuleAddress, chainName, cosmos.ProposalVoteYes, govv1.StatusPassed, false) s.Require().Error(err) s.Require().Contains(err.Error(), "expected gov account") @@ -606,17 +606,17 @@ func (s *SingleValidatorProviderSuite) TestProviderOwnerChecks() { // update to top N using proposal upgradeMsg = &providertypes.MsgUpdateConsumer{ - Owner: chainsuite.GovModuleAddress, - NewOwnerAddress: chainsuite.GovModuleAddress, + Owner: chainsuite.ProviderGovModuleAddress, + NewOwnerAddress: chainsuite.ProviderGovModuleAddress, ConsumerId: consumerId, PowerShapingParameters: powerShapingParams, } - s.Require().NoError(s.Provider.ExecuteProposalMsg(s.GetContext(), upgradeMsg, chainsuite.GovModuleAddress, chainName, cosmos.ProposalVoteYes, govv1.StatusPassed, false)) + s.Require().NoError(s.Provider.ExecuteProposalMsg(s.GetContext(), upgradeMsg, chainsuite.ProviderGovModuleAddress, chainName, cosmos.ProposalVoteYes, govv1.StatusPassed, false)) consumerChain, err = s.Provider.GetConsumerChain(s.GetContext(), consumerId) s.Require().NoError(err) s.Require().Equal(providertypes.CONSUMER_PHASE_LAUNCHED.String(), consumerChain.Phase) s.Require().Equal(powerShapingParams.Top_N, uint32(consumerChain.PowerShapingParams.TopN)) - s.Require().Equal(chainsuite.GovModuleAddress, consumerChain.OwnerAddress) + s.Require().Equal(chainsuite.ProviderGovModuleAddress, consumerChain.OwnerAddress) } // Tests adding and updating infraction parameters with MsgCreateConsumer and MsgUpdateConsumer. @@ -627,7 +627,7 @@ func (s *SingleValidatorProviderSuite) TestProviderOwnerChecks() { // Confirms that existing queued parameters, scheduled for update after the unbonding period, can be canceled if a new MsgUpdateConsumer // is sent with values identical to the current infraction parameters for that chain. func (s *SingleValidatorProviderSuite) TestInfractionParameters() { - testAcc, testAccKey, err := s.GetUnusedTestingAddresss() + testAcc, testAccKey, err := s.Provider.GetUnusedTestingAddresss() s.Require().NoError(err) defaultInfractionParams := defaultInfractionParams() diff --git a/tests/interchain/provider_suite.go b/tests/interchain/provider_suite.go index 88707609ff..0d69027973 100644 --- a/tests/interchain/provider_suite.go +++ b/tests/interchain/provider_suite.go @@ -3,9 +3,9 @@ package interchain import ( "context" "cosmos/interchain-security/tests/interchain/chainsuite" - "fmt" - "sync" + "strconv" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" "github.com/stretchr/testify/suite" ) @@ -14,18 +14,15 @@ type ProviderSuite struct { Provider *chainsuite.Chain ValidatorNodes int ctx context.Context - walletMtx sync.Mutex - walletsInUse map[int]bool } func (s *ProviderSuite) SetupSuite() { - s.walletsInUse = make(map[int]bool) ctx, err := chainsuite.NewSuiteContext(&s.Suite) s.Require().NoError(err) s.ctx = ctx // create and start provider chain - s.Provider, err = chainsuite.CreateProviderChain(s.GetContext(), s.T(), chainsuite.GetProviderSpec(s.ValidatorNodes)) + s.Provider, err = chainsuite.CreateChain(s.GetContext(), s.T(), chainsuite.GetProviderSpec(s.ValidatorNodes, providerModifiedGenesis())) s.Require().NoError(err) } @@ -34,17 +31,19 @@ func (s *ProviderSuite) GetContext() context.Context { return s.ctx } -// GetUnusedTestingAddresss retrieves an unused wallet address and its key name safely -func (s *ProviderSuite) GetUnusedTestingAddresss() (formattedAddress string, keyName string, err error) { - s.walletMtx.Lock() - defer s.walletMtx.Unlock() - - for i, wallet := range s.Provider.TestWallets { - if !s.walletsInUse[i] { - s.walletsInUse[i] = true - return wallet.FormattedAddress(), wallet.KeyName(), nil - } +func providerModifiedGenesis() []cosmos.GenesisKV { + return []cosmos.GenesisKV{ + cosmos.NewGenesisKV("app_state.staking.params.unbonding_time", chainsuite.ProviderUnbondingTime.String()), + cosmos.NewGenesisKV("app_state.gov.params.voting_period", chainsuite.GovVotingPeriod.String()), + cosmos.NewGenesisKV("app_state.gov.params.max_deposit_period", chainsuite.GovDepositPeriod.String()), + cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.denom", chainsuite.Stake), + cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.amount", strconv.Itoa(chainsuite.GovMinDepositAmount)), + cosmos.NewGenesisKV("app_state.slashing.params.signed_blocks_window", strconv.Itoa(chainsuite.ProviderSlashingWindow)), + cosmos.NewGenesisKV("app_state.slashing.params.downtime_jail_duration", chainsuite.DowntimeJailDuration.String()), + cosmos.NewGenesisKV("app_state.slashing.params.slash_fraction_double_sign", chainsuite.SlashFractionDoubleSign), + cosmos.NewGenesisKV("app_state.provider.params.slash_meter_replenish_period", chainsuite.ProviderReplenishPeriod), + cosmos.NewGenesisKV("app_state.provider.params.slash_meter_replenish_fraction", chainsuite.ProviderReplenishFraction), + cosmos.NewGenesisKV("app_state.provider.params.blocks_per_epoch", "1"), + cosmos.NewGenesisKV("app_state.staking.params.max_validators", "1"), } - - return "", "", fmt.Errorf("no unused wallets available") } diff --git a/tests/interchain/provider_utils.go b/tests/interchain/provider_utils.go index fb372f3cfc..7f21e720bc 100644 --- a/tests/interchain/provider_utils.go +++ b/tests/interchain/provider_utils.go @@ -2,9 +2,12 @@ package interchain import ( "cosmos/interchain-security/tests/interchain/chainsuite" + "fmt" + "strings" "time" "cosmossdk.io/math" + sdkmath "cosmossdk.io/math" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" providertypes "github.com/cosmos/interchain-security/v6/x/ccv/provider/types" ) @@ -36,9 +39,9 @@ func consumerInitParamsTemplate(spawnTime *time.Time) *providertypes.ConsumerIni InitialHeight: clienttypes.NewHeight(1, 1), GenesisHash: []byte("gen_hash"), BinaryHash: []byte("bin_hash"), - UnbondingPeriod: 10 * time.Second, - CcvTimeoutPeriod: time.Duration(100000000000), - TransferTimeoutPeriod: time.Duration(100000000000), + UnbondingPeriod: 1728000000000000, + CcvTimeoutPeriod: 2419200000000000, + TransferTimeoutPeriod: 3600000000000, ConsumerRedistributionFraction: "0.75", BlocksPerDistributionTransmission: 10, HistoricalEntries: 10000, @@ -105,3 +108,12 @@ func convertJsonToInfractionParameters(jsonParams chainsuite.InfractionParams) * }, } } + +func StrToSDKInt(s string) (sdkmath.Int, error) { + s, _, _ = strings.Cut(s, ".") + i, ok := sdkmath.NewIntFromString(s) + if !ok { + return sdkmath.Int{}, fmt.Errorf("s: %s", s) + } + return i, nil +} diff --git a/x/ccv/consumer/keeper/genesis.go b/x/ccv/consumer/keeper/genesis.go index 5e850ba351..74bc631724 100644 --- a/x/ccv/consumer/keeper/genesis.go +++ b/x/ccv/consumer/keeper/genesis.go @@ -3,7 +3,10 @@ package keeper import ( "fmt" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" + conntypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" abci "github.com/cometbft/cometbft/abci/types" @@ -55,11 +58,31 @@ func (k Keeper) InitGenesis(ctx sdk.Context, state *types.GenesisState) []abci.V // initialValSet is checked in NewChain case by ValidateGenesis // start a new chain if state.NewChain { - // create the provider client in InitGenesis for new consumer chain. CCV Handshake must be established with this client id. - clientID, err := k.clientKeeper.CreateClient(ctx, state.Provider.ClientState, state.Provider.ConsensusState) - if err != nil { - // If the client creation fails, the chain MUST NOT start - panic(err) + var clientID string + if state.ConnectionId == "" { + // create the provider client in InitGenesis for new consumer chain. CCV Handshake must be established with this client id. + cid, err := k.clientKeeper.CreateClient(ctx, state.Provider.ClientState, state.Provider.ConsensusState) + if err != nil { + // If the client creation fails, the chain MUST NOT start + panic(err) + } + clientID = cid + + k.Logger(ctx).Info("create new provider chain client", + "client id", clientID, + ) + } else { + // if connection id is provided, then the client is already created + connectionEnd, found := k.connectionKeeper.GetConnection(ctx, state.ConnectionId) + if !found { + panic(errorsmod.Wrapf(conntypes.ErrConnectionNotFound, "could not find connection(%s)", state.ConnectionId)) + } + clientID = connectionEnd.ClientId + + k.Logger(ctx).Info("use existing client and connection to provider chain", + "client id", clientID, + "connection id", state.ConnectionId, + ) } // set provider client id. @@ -68,6 +91,26 @@ func (k Keeper) InitGenesis(ctx sdk.Context, state *types.GenesisState) []abci.V // set default value for valset update ID k.SetHeightValsetUpdateID(ctx, uint64(ctx.BlockHeight()), uint64(0)) + if state.ConnectionId != "" { + // initiate CCV channel handshake + ccvChannelOpenInitMsg := channeltypes.NewMsgChannelOpenInit( + ccv.ConsumerPortID, + ccv.Version, + channeltypes.ORDERED, + []string{state.ConnectionId}, + ccv.ProviderPortID, + "", // signer unused + ) + _, err := k.ChannelOpenInit(ctx, ccvChannelOpenInitMsg) + if err != nil { + panic(err) + } + + // Note that if the connection ID is not provider, we cannot initiate + // the connection handshake as the counterparty client ID is unknown + // at this point. The connection handshake must be initiated by a relayer. + } + } else { // chain restarts with the CCV channel established if state.ProviderChannelId != "" { diff --git a/x/ccv/consumer/types/genesis.go b/x/ccv/consumer/types/genesis.go index 2d5ae3a848..907756b9ca 100644 --- a/x/ccv/consumer/types/genesis.go +++ b/x/ccv/consumer/types/genesis.go @@ -64,7 +64,13 @@ func NewInitialGenesisState(cs *ibctmtypes.ClientState, consState *ibctmtypes.Co // expect the following optional and mandatory genesis states: // // 1. New chain starts: -// - Params, InitialValset, provider client state, provider consensus state // mandatory +// - Params, InitialValset // mandatory +// +// 1a. ConnectionId is empty +// - provider client state, provider consensus state // mandatory +// +// 1b. ConnectionId is not empty +// - provider client state, provider consensus state // nil // // 2. Chain restarts with CCV handshake still in progress: // - Params, InitialValset, ProviderID, HeightToValidatorSetUpdateID // mandatory @@ -73,8 +79,6 @@ func NewInitialGenesisState(cs *ibctmtypes.ClientState, consState *ibctmtypes.Co // 3. Chain restarts with CCV handshake completed: // - Params, InitialValset, ProviderID, channelID, HeightToValidatorSetUpdateID // mandatory // - MaturingVSCPackets, OutstandingDowntime, PendingConsumerPacket, LastTransmissionBlockHeight // optional -// - func (gs GenesisState) Validate() error { if !gs.Params.Enabled { return nil @@ -87,24 +91,45 @@ func (gs GenesisState) Validate() error { } if gs.NewChain { - if gs.Provider.ClientState == nil { - return errorsmod.Wrap(ccv.ErrInvalidGenesis, "provider client state cannot be nil for new chain") - } - if err := gs.Provider.ClientState.Validate(); err != nil { - return errorsmod.Wrapf(ccv.ErrInvalidGenesis, "provider client state invalid for new chain %s", err.Error()) - } - if gs.Provider.ConsensusState == nil { - return errorsmod.Wrap(ccv.ErrInvalidGenesis, "provider consensus state cannot be nil for new chain") - } - if err := gs.Provider.ConsensusState.ValidateBasic(); err != nil { - return errorsmod.Wrapf(ccv.ErrInvalidGenesis, "provider consensus state invalid for new chain %s", err.Error()) + if gs.ConnectionId == "" { + // connection ID is not provided + if gs.Provider.ClientState == nil { + return errorsmod.Wrap(ccv.ErrInvalidGenesis, "provider client state cannot be nil for new chain") + } + if err := gs.Provider.ClientState.Validate(); err != nil { + return errorsmod.Wrapf(ccv.ErrInvalidGenesis, "provider client state invalid for new chain %s", err.Error()) + } + if gs.Provider.ConsensusState == nil { + return errorsmod.Wrap(ccv.ErrInvalidGenesis, "provider consensus state cannot be nil for new chain") + } + if err := gs.Provider.ConsensusState.ValidateBasic(); err != nil { + return errorsmod.Wrapf(ccv.ErrInvalidGenesis, "provider consensus state invalid for new chain %s", err.Error()) + } + } else { + // connection ID is provided + if gs.Provider.ClientState != nil { + return errorsmod.Wrap(ccv.ErrInvalidGenesis, "provider client state must be nil when connection id is provided") + } + if gs.Provider.ConsensusState != nil { + return errorsmod.Wrap(ccv.ErrInvalidGenesis, "provider consensus state must be nil when connection id is provided") + } + if err := ccv.ValidateConnectionIdentifier(gs.ConnectionId); err != nil { + return errorsmod.Wrapf(ccv.ErrInvalidGenesis, "invalid connection id: %s", err.Error()) + } } + if gs.ProviderClientId != "" { return errorsmod.Wrap(ccv.ErrInvalidGenesis, "provider client id cannot be set for new chain. It must be established on handshake") } if gs.ProviderChannelId != "" { return errorsmod.Wrap(ccv.ErrInvalidGenesis, "provider channel id cannot be set for new chain. It must be established on handshake") } + if len(gs.HeightToValsetUpdateId) != 0 { + return errorsmod.Wrap(ccv.ErrInvalidGenesis, "HeightToValsetUpdateId must be nil for new chain") + } + if len(gs.OutstandingDowntimeSlashing) != 0 { + return errorsmod.Wrap(ccv.ErrInvalidGenesis, "OutstandingDowntimeSlashing must be nil for new chain") + } if len(gs.PendingConsumerPackets.List) != 0 { return errorsmod.Wrap(ccv.ErrInvalidGenesis, "pending consumer packets must be empty for new chain") } @@ -121,11 +146,11 @@ func (gs GenesisState) Validate() error { if handshakeInProgress { if len(gs.OutstandingDowntimeSlashing) != 0 { return errorsmod.Wrap( - ccv.ErrInvalidGenesis, "outstanding downtime must be empty when handshake isn't completed") + ccv.ErrInvalidGenesis, "outstanding downtime must be empty when handshake in progress") } if gs.LastTransmissionBlockHeight.Height != 0 { return errorsmod.Wrap( - ccv.ErrInvalidGenesis, "last transmission block height must be zero when handshake isn't completed") + ccv.ErrInvalidGenesis, "last transmission block height must be zero when handshake in progress") } if len(gs.PendingConsumerPackets.List) != 0 { for _, packet := range gs.PendingConsumerPackets.List { diff --git a/x/ccv/consumer/types/genesis.pb.go b/x/ccv/consumer/types/genesis.pb.go index 33350f3e2c..17415f24f9 100644 --- a/x/ccv/consumer/types/genesis.pb.go +++ b/x/ccv/consumer/types/genesis.pb.go @@ -48,9 +48,14 @@ type GenesisState struct { PendingConsumerPackets ConsumerPacketDataList `protobuf:"bytes,11,opt,name=pending_consumer_packets,json=pendingConsumerPackets,proto3" json:"pending_consumer_packets"` // LastTransmissionBlockHeight nil on new chain, filled in on restart. LastTransmissionBlockHeight LastTransmissionBlockHeight `protobuf:"bytes,12,opt,name=last_transmission_block_height,json=lastTransmissionBlockHeight,proto3" json:"last_transmission_block_height"` - // flag indicating whether the consumer CCV module starts in pre-CCV state + // Flag indicating whether the consumer CCV module starts in pre-CCV state PreCCV bool `protobuf:"varint,13,opt,name=preCCV,proto3" json:"preCCV,omitempty"` Provider types.ProviderInfo `protobuf:"bytes,14,opt,name=provider,proto3" json:"provider"` + // The ID of the connection end on the consumer chain on top of which the + // CCV channel will be established. If connection_id == "", a new client of + // the provider chain and a new connection on top of this client are created. + // The new client is initialized using provider.client_state and provider.consensus_state. + ConnectionId string `protobuf:"bytes,15,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -156,6 +161,13 @@ func (m *GenesisState) GetProvider() types.ProviderInfo { return types.ProviderInfo{} } +func (m *GenesisState) GetConnectionId() string { + if m != nil { + return m.ConnectionId + } + return "" +} + // HeightValsetUpdateID represents a mapping internal to the consumer CCV module // which links a block height to each recv valset update id. type HeightToValsetUpdateID struct { @@ -365,54 +377,55 @@ func init() { } var fileDescriptor_2db73a6057a27482 = []byte{ - // 741 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0x4d, 0x6b, 0xdb, 0x4a, - 0x14, 0xb5, 0x12, 0x3d, 0x47, 0x9e, 0xe4, 0x05, 0x3f, 0xe5, 0x61, 0xf4, 0x62, 0x9e, 0x63, 0x5c, - 0x0a, 0xa6, 0xb4, 0x52, 0x9d, 0xd2, 0x52, 0x28, 0x2d, 0x6d, 0x1c, 0x68, 0x6c, 0x02, 0x0d, 0xce, - 0x47, 0x21, 0x9b, 0x61, 0x2c, 0x4d, 0xa4, 0x21, 0xd2, 0x8c, 0xd0, 0x8c, 0xe5, 0x86, 0xd2, 0x4d, - 0xb7, 0xdd, 0xf4, 0x67, 0x65, 0x99, 0x65, 0x57, 0xa1, 0x24, 0x7f, 0xa4, 0x68, 0x34, 0xb2, 0x13, - 0xe2, 0x04, 0xef, 0x34, 0xbe, 0xe7, 0x9e, 0x7b, 0x74, 0xee, 0xd1, 0x18, 0x74, 0x08, 0x15, 0x38, - 0x71, 0x03, 0x44, 0x28, 0xe4, 0xd8, 0x1d, 0x25, 0x44, 0x9c, 0x39, 0xae, 0x9b, 0x3a, 0x2e, 0xa3, - 0x7c, 0x14, 0xe1, 0xc4, 0x49, 0x3b, 0x8e, 0x8f, 0x29, 0xe6, 0x84, 0xdb, 0x71, 0xc2, 0x04, 0x33, - 0x1f, 0xcd, 0x68, 0xb1, 0x5d, 0x37, 0xb5, 0x8b, 0x16, 0x3b, 0xed, 0xac, 0x3f, 0xbf, 0x8f, 0x37, - 0xed, 0x38, 0x3c, 0x40, 0x09, 0xf6, 0xe0, 0x04, 0x2e, 0x69, 0xd7, 0x1d, 0x32, 0x74, 0x9d, 0x90, - 0xf8, 0x81, 0x70, 0x43, 0x82, 0xa9, 0xe0, 0x8e, 0xc0, 0xd4, 0xc3, 0x49, 0x44, 0xa8, 0xc8, 0xba, - 0xa6, 0x27, 0xd5, 0xf0, 0xaf, 0xcf, 0x7c, 0x26, 0x1f, 0x9d, 0xec, 0x49, 0xfd, 0xfa, 0xf8, 0x81, - 0xc1, 0x63, 0x92, 0x60, 0x05, 0xdb, 0xf0, 0x19, 0xf3, 0x43, 0xec, 0xc8, 0xd3, 0x70, 0x74, 0xe2, - 0x08, 0x12, 0x61, 0x2e, 0x50, 0x14, 0x2b, 0x40, 0xfd, 0xc6, 0x74, 0x34, 0x74, 0x89, 0x23, 0xce, - 0x62, 0xac, 0x2c, 0x68, 0x5d, 0x96, 0xc1, 0xca, 0xc7, 0xdc, 0x94, 0x7d, 0x81, 0x04, 0x36, 0x77, - 0x40, 0x39, 0x46, 0x09, 0x8a, 0xb8, 0xa5, 0x35, 0xb5, 0xf6, 0xf2, 0xe6, 0x13, 0xfb, 0x3e, 0x93, - 0xd2, 0x8e, 0xdd, 0x55, 0x2f, 0xbe, 0x27, 0x3b, 0xb6, 0xf4, 0xf3, 0xcb, 0x8d, 0xd2, 0x40, 0xf5, - 0x9b, 0x4f, 0x81, 0x19, 0x27, 0x2c, 0x25, 0x1e, 0x4e, 0x60, 0x6e, 0x04, 0x24, 0x9e, 0xb5, 0xd0, - 0xd4, 0xda, 0x95, 0x41, 0xb5, 0xa8, 0x74, 0x65, 0xa1, 0xe7, 0x99, 0x36, 0x58, 0x9b, 0xa2, 0x03, - 0x44, 0x29, 0x0e, 0x33, 0xf8, 0xa2, 0x84, 0xff, 0x33, 0x81, 0xe7, 0x95, 0x9e, 0x67, 0xd6, 0x41, - 0x85, 0xe2, 0x31, 0x94, 0xba, 0x2c, 0xbd, 0xa9, 0xb5, 0x8d, 0x81, 0x41, 0xf1, 0xb8, 0x9b, 0x9d, - 0xcd, 0x6f, 0x60, 0x3d, 0xc0, 0xd9, 0x02, 0xa0, 0x60, 0x30, 0x45, 0x21, 0xc7, 0x02, 0x8e, 0x62, - 0x0f, 0x09, 0x9c, 0x71, 0x56, 0x9a, 0x8b, 0xed, 0xe5, 0xcd, 0x37, 0xf6, 0x1c, 0xdb, 0xb7, 0x77, - 0x24, 0xcd, 0x01, 0x3b, 0x92, 0x24, 0x87, 0x92, 0xa3, 0xb7, 0xad, 0xde, 0xb4, 0x16, 0xcc, 0xaa, - 0x7a, 0xe6, 0x77, 0x0d, 0xfc, 0xcf, 0x46, 0x82, 0x0b, 0x44, 0x3d, 0x42, 0x7d, 0xe8, 0xb1, 0x31, - 0xcd, 0xb6, 0x02, 0x79, 0x88, 0x78, 0x40, 0xa8, 0x6f, 0x01, 0x29, 0xe1, 0xf5, 0x5c, 0x12, 0x3e, - 0x4d, 0x99, 0xb6, 0x15, 0x91, 0x9a, 0x5f, 0x67, 0x77, 0x4b, 0xfb, 0x6a, 0x84, 0xf9, 0x15, 0x58, - 0x31, 0xce, 0xe7, 0x17, 0x6c, 0x30, 0x46, 0xee, 0x29, 0x16, 0xdc, 0x5a, 0x96, 0xab, 0x9d, 0xcf, - 0x81, 0xe9, 0x8e, 0xb3, 0xde, 0x6d, 0x24, 0xd0, 0x2e, 0xe1, 0xa2, 0x70, 0x40, 0x8d, 0xb8, 0x0d, - 0xe2, 0xe6, 0x0f, 0x0d, 0x34, 0x42, 0xc4, 0x05, 0x14, 0x09, 0xa2, 0x3c, 0x22, 0x9c, 0x13, 0x46, - 0xe1, 0x30, 0x64, 0xee, 0x29, 0xcc, 0x4d, 0xb3, 0x56, 0xa4, 0x86, 0xf7, 0x73, 0x69, 0xd8, 0x45, - 0x5c, 0x1c, 0xdc, 0x60, 0xda, 0xca, 0x88, 0xf2, 0xd5, 0x14, 0x56, 0x84, 0xf7, 0x43, 0xcc, 0x1a, - 0x28, 0xc7, 0x09, 0xee, 0x76, 0x8f, 0xac, 0xbf, 0x65, 0x50, 0xd4, 0xc9, 0xec, 0x03, 0xa3, 0x08, - 0x96, 0xb5, 0x2a, 0xe5, 0xb4, 0x1f, 0x4a, 0xfb, 0x9e, 0xc2, 0xf6, 0xe8, 0x09, 0x53, 0x63, 0x27, - 0xfd, 0x7d, 0xdd, 0xf8, 0xab, 0x5a, 0xee, 0xeb, 0x46, 0xb9, 0xba, 0xd4, 0xd7, 0x8d, 0xa5, 0xaa, - 0xd1, 0xd7, 0x0d, 0xa3, 0x5a, 0x69, 0x1d, 0x83, 0xda, 0xec, 0x0c, 0x65, 0xaa, 0x94, 0x15, 0xd9, - 0x97, 0xa6, 0x0f, 0xd4, 0xc9, 0x6c, 0x83, 0xea, 0x9d, 0xc8, 0x2e, 0x48, 0xc4, 0x6a, 0x7a, 0x2b, - 0x67, 0xad, 0x43, 0xb0, 0x36, 0x23, 0x1c, 0xe6, 0x3b, 0x50, 0x4f, 0x51, 0x48, 0x3c, 0x24, 0x58, - 0x22, 0x77, 0x8f, 0x29, 0x1f, 0x71, 0x88, 0x3c, 0x2f, 0xc1, 0x3c, 0xff, 0xae, 0x2b, 0x83, 0xff, - 0x26, 0x90, 0x6e, 0x81, 0xf8, 0x90, 0x03, 0x5a, 0x2f, 0x41, 0x7d, 0xf7, 0x61, 0x37, 0x6f, 0xe8, - 0x5e, 0x2c, 0x74, 0xb7, 0x86, 0xa0, 0x36, 0x3b, 0x2b, 0xe6, 0x0e, 0xd0, 0x43, 0xc2, 0x33, 0x7c, - 0x96, 0x7a, 0x7b, 0xbe, 0x1b, 0xa5, 0x60, 0x50, 0x4e, 0x4b, 0x86, 0xad, 0xcf, 0xe7, 0x57, 0x0d, - 0xed, 0xe2, 0xaa, 0xa1, 0xfd, 0xbe, 0x6a, 0x68, 0x3f, 0xaf, 0x1b, 0xa5, 0x8b, 0xeb, 0x46, 0xe9, - 0xd7, 0x75, 0xa3, 0x74, 0xfc, 0xd6, 0x27, 0x22, 0x18, 0x0d, 0x6d, 0x97, 0x45, 0x8e, 0xcb, 0x78, - 0xc4, 0xb8, 0x33, 0x1d, 0xf3, 0x6c, 0x72, 0x7f, 0xa6, 0xaf, 0x9c, 0x2f, 0xb7, 0xff, 0x15, 0xe4, - 0x6d, 0x38, 0x2c, 0xcb, 0xeb, 0xf0, 0xc5, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfd, 0xe7, 0x1e, - 0x4d, 0x46, 0x06, 0x00, 0x00, + // 763 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0x4d, 0x6b, 0xeb, 0x46, + 0x14, 0xb5, 0x12, 0xd5, 0x91, 0x27, 0x1f, 0x75, 0x27, 0xc5, 0xa8, 0x31, 0x75, 0x8c, 0x43, 0xc1, + 0x94, 0x56, 0xaa, 0x53, 0x5a, 0x0a, 0xa5, 0xa5, 0x8d, 0x03, 0x8d, 0x4d, 0xa0, 0xc1, 0xf9, 0x28, + 0x64, 0x23, 0xc6, 0xa3, 0x89, 0x34, 0x44, 0x9a, 0x11, 0x9a, 0xb1, 0xdc, 0x50, 0xba, 0x69, 0x97, + 0x6f, 0xf3, 0x7e, 0x56, 0x96, 0x59, 0xbe, 0xd5, 0xe3, 0x91, 0xfc, 0x91, 0x87, 0x46, 0x23, 0x3b, + 0x21, 0x4e, 0xf0, 0xce, 0x57, 0xf7, 0xdc, 0x73, 0xef, 0x9c, 0x7b, 0x66, 0x0c, 0x7a, 0x94, 0x49, + 0x92, 0xe2, 0x10, 0x51, 0xe6, 0x09, 0x82, 0x27, 0x29, 0x95, 0x37, 0x2e, 0xc6, 0x99, 0x8b, 0x39, + 0x13, 0x93, 0x98, 0xa4, 0x6e, 0xd6, 0x73, 0x03, 0xc2, 0x88, 0xa0, 0xc2, 0x49, 0x52, 0x2e, 0x39, + 0xdc, 0x5b, 0x50, 0xe2, 0x60, 0x9c, 0x39, 0x65, 0x89, 0x93, 0xf5, 0x76, 0xbe, 0x7b, 0x89, 0x37, + 0xeb, 0xb9, 0x22, 0x44, 0x29, 0xf1, 0xbd, 0x19, 0x5c, 0xd1, 0xee, 0xb8, 0x74, 0x8c, 0xdd, 0x88, + 0x06, 0xa1, 0xc4, 0x11, 0x25, 0x4c, 0x0a, 0x57, 0x12, 0xe6, 0x93, 0x34, 0xa6, 0x4c, 0xe6, 0x55, + 0xf3, 0x48, 0x17, 0x7c, 0x1e, 0xf0, 0x80, 0xab, 0x9f, 0x6e, 0xfe, 0x4b, 0x7f, 0xfd, 0xea, 0x95, + 0xc6, 0x53, 0x9a, 0x12, 0x0d, 0xdb, 0x0d, 0x38, 0x0f, 0x22, 0xe2, 0xaa, 0x68, 0x3c, 0xb9, 0x72, + 0x25, 0x8d, 0x89, 0x90, 0x28, 0x4e, 0x34, 0xa0, 0xf9, 0xa8, 0x3b, 0x1a, 0x63, 0xea, 0xca, 0x9b, + 0x84, 0x68, 0x09, 0x3a, 0xff, 0xaf, 0x81, 0x8d, 0x3f, 0x0a, 0x51, 0x4e, 0x25, 0x92, 0x04, 0x1e, + 0x81, 0x6a, 0x82, 0x52, 0x14, 0x0b, 0xdb, 0x68, 0x1b, 0xdd, 0xf5, 0xfd, 0xaf, 0x9d, 0x97, 0x44, + 0xca, 0x7a, 0x4e, 0x5f, 0x1f, 0xfc, 0x44, 0x55, 0x1c, 0x98, 0xb7, 0xef, 0x77, 0x2b, 0x23, 0x5d, + 0x0f, 0xbf, 0x01, 0x30, 0x49, 0x79, 0x46, 0x7d, 0x92, 0x7a, 0x85, 0x10, 0x1e, 0xf5, 0xed, 0x95, + 0xb6, 0xd1, 0xad, 0x8d, 0xea, 0x65, 0xa6, 0xaf, 0x12, 0x03, 0x1f, 0x3a, 0x60, 0x7b, 0x8e, 0x0e, + 0x11, 0x63, 0x24, 0xca, 0xe1, 0xab, 0x0a, 0xfe, 0xd9, 0x0c, 0x5e, 0x64, 0x06, 0x3e, 0x6c, 0x82, + 0x1a, 0x23, 0x53, 0x4f, 0xcd, 0x65, 0x9b, 0x6d, 0xa3, 0x6b, 0x8d, 0x2c, 0x46, 0xa6, 0xfd, 0x3c, + 0x86, 0xff, 0x82, 0x9d, 0x90, 0xe4, 0x0b, 0xf0, 0x24, 0xf7, 0x32, 0x14, 0x09, 0x22, 0xbd, 0x49, + 0xe2, 0x23, 0x49, 0x72, 0xce, 0x5a, 0x7b, 0xb5, 0xbb, 0xbe, 0xff, 0xb3, 0xb3, 0xc4, 0xf6, 0x9d, + 0x23, 0x45, 0x73, 0xc6, 0x2f, 0x14, 0xc9, 0xb9, 0xe2, 0x18, 0x1c, 0xea, 0x93, 0x36, 0xc2, 0x45, + 0x59, 0x1f, 0xfe, 0x67, 0x80, 0x2f, 0xf9, 0x44, 0x0a, 0x89, 0x98, 0x4f, 0x59, 0xe0, 0xf9, 0x7c, + 0xca, 0xf2, 0xad, 0x78, 0x22, 0x42, 0x22, 0xa4, 0x2c, 0xb0, 0x81, 0x1a, 0xe1, 0xa7, 0xa5, 0x46, + 0xf8, 0x73, 0xce, 0x74, 0xa8, 0x89, 0x74, 0xff, 0x26, 0x7f, 0x9e, 0x3a, 0xd5, 0x2d, 0xe0, 0x3f, + 0xc0, 0x4e, 0x48, 0xd1, 0xbf, 0x64, 0xf3, 0x12, 0x84, 0xaf, 0x89, 0x14, 0xf6, 0xba, 0x5a, 0xed, + 0x72, 0x0a, 0xcc, 0x77, 0x9c, 0xd7, 0x1e, 0x22, 0x89, 0x8e, 0xa9, 0x90, 0xa5, 0x02, 0xba, 0xc5, + 0x53, 0x90, 0x80, 0x6f, 0x0c, 0xd0, 0x8a, 0x90, 0x90, 0x9e, 0x4c, 0x11, 0x13, 0x31, 0x15, 0x82, + 0x72, 0xe6, 0x8d, 0x23, 0x8e, 0xaf, 0xbd, 0x42, 0x34, 0x7b, 0x43, 0xcd, 0xf0, 0xdb, 0x52, 0x33, + 0x1c, 0x23, 0x21, 0xcf, 0x1e, 0x31, 0x1d, 0xe4, 0x44, 0xc5, 0x6a, 0x4a, 0x29, 0xa2, 0x97, 0x21, + 0xb0, 0x01, 0xaa, 0x49, 0x4a, 0xfa, 0xfd, 0x0b, 0x7b, 0x53, 0x19, 0x45, 0x47, 0x70, 0x08, 0xac, + 0xd2, 0x58, 0xf6, 0x96, 0x1a, 0xa7, 0xfb, 0x9a, 0xdb, 0x4f, 0x34, 0x76, 0xc0, 0xae, 0xb8, 0x6e, + 0x3b, 0xab, 0x87, 0x7b, 0x60, 0x13, 0x73, 0xc6, 0x08, 0x96, 0xf9, 0x49, 0xa9, 0x6f, 0x7f, 0xaa, + 0x9c, 0xbb, 0x31, 0xff, 0x38, 0xf0, 0x87, 0xa6, 0xf5, 0x49, 0xbd, 0x3a, 0x34, 0xad, 0x6a, 0x7d, + 0x6d, 0x68, 0x5a, 0x6b, 0x75, 0x6b, 0x68, 0x5a, 0x56, 0xbd, 0xd6, 0xb9, 0x04, 0x8d, 0xc5, 0x46, + 0xcb, 0x47, 0xd7, 0x7a, 0xe5, 0xd7, 0xd1, 0x1c, 0xe9, 0x08, 0x76, 0x41, 0xfd, 0x99, 0xaf, 0x57, + 0x14, 0x62, 0x2b, 0x7b, 0x62, 0xc6, 0xce, 0x39, 0xd8, 0x5e, 0xe0, 0x20, 0xf8, 0x2b, 0x68, 0x66, + 0x28, 0xa2, 0x3e, 0x92, 0x3c, 0x55, 0x06, 0x21, 0x4c, 0x4c, 0x84, 0x87, 0x7c, 0x3f, 0x25, 0xa2, + 0xb8, 0xfc, 0xb5, 0xd1, 0x17, 0x33, 0x48, 0xbf, 0x44, 0xfc, 0x5e, 0x00, 0x3a, 0x3f, 0x80, 0xe6, + 0xf1, 0xeb, 0x92, 0x3f, 0x9a, 0x7b, 0xb5, 0x9c, 0xbb, 0x33, 0x06, 0x8d, 0xc5, 0x86, 0x82, 0x47, + 0xc0, 0x8c, 0xa8, 0xc8, 0xf1, 0xf9, 0xd5, 0x70, 0x96, 0x7b, 0x76, 0x4a, 0x06, 0xbd, 0x0e, 0xc5, + 0x70, 0xf0, 0xd7, 0xed, 0x7d, 0xcb, 0xb8, 0xbb, 0x6f, 0x19, 0x1f, 0xee, 0x5b, 0xc6, 0xdb, 0x87, + 0x56, 0xe5, 0xee, 0xa1, 0x55, 0x79, 0xf7, 0xd0, 0xaa, 0x5c, 0xfe, 0x12, 0x50, 0x19, 0x4e, 0xc6, + 0x0e, 0xe6, 0xb1, 0x8b, 0xb9, 0x88, 0xb9, 0x70, 0xe7, 0x6d, 0xbe, 0x9d, 0x3d, 0xb2, 0xd9, 0x8f, + 0xee, 0xdf, 0x4f, 0xff, 0x3a, 0xd4, 0x93, 0x39, 0xae, 0xaa, 0x37, 0xf3, 0xfb, 0x8f, 0x01, 0x00, + 0x00, 0xff, 0xff, 0xd1, 0xf7, 0xbc, 0x20, 0x6b, 0x06, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -435,6 +448,13 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ConnectionId) > 0 { + i -= len(m.ConnectionId) + copy(dAtA[i:], m.ConnectionId) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.ConnectionId))) + i-- + dAtA[i] = 0x7a + } { size, err := m.Provider.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -719,6 +739,10 @@ func (m *GenesisState) Size() (n int) { } l = m.Provider.Size() n += 1 + l + sovGenesis(uint64(l)) + l = len(m.ConnectionId) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } return n } @@ -1116,6 +1140,38 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + 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 ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConnectionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) diff --git a/x/ccv/consumer/types/genesis_test.go b/x/ccv/consumer/types/genesis_test.go index bbe031d0d7..8faa41c3a7 100644 --- a/x/ccv/consumer/types/genesis_test.go +++ b/x/ccv/consumer/types/genesis_test.go @@ -231,6 +231,90 @@ func TestValidateInitialGenesisState(t *testing.T) { )), true, }, + { + "invalid new consumer genesis state: connection id is invalid", + &types.GenesisState{ + Params: params, + ProviderClientId: "", + ProviderChannelId: "", + NewChain: true, + Provider: ccv.ProviderInfo{ + ClientState: nil, + ConsensusState: nil, + InitialValSet: valUpdates, + }, + HeightToValsetUpdateId: nil, + OutstandingDowntimeSlashing: nil, + PendingConsumerPackets: types.ConsumerPacketDataList{}, + LastTransmissionBlockHeight: types.LastTransmissionBlockHeight{}, + PreCCV: false, + ConnectionId: "invalid_connection/", + }, + true, + }, + { + "invalid new consumer genesis state: client state provided with connection id", + &types.GenesisState{ + Params: params, + ProviderClientId: "", + ProviderChannelId: "", + NewChain: true, + Provider: ccv.ProviderInfo{ + ClientState: cs, + ConsensusState: nil, + InitialValSet: valUpdates, + }, + HeightToValsetUpdateId: nil, + OutstandingDowntimeSlashing: nil, + PendingConsumerPackets: types.ConsumerPacketDataList{}, + LastTransmissionBlockHeight: types.LastTransmissionBlockHeight{}, + PreCCV: false, + ConnectionId: "connection-1", + }, + true, + }, + { + "invalid new consumer genesis state: client state provided with connection id", + &types.GenesisState{ + Params: params, + ProviderClientId: "", + ProviderChannelId: "", + NewChain: true, + Provider: ccv.ProviderInfo{ + ClientState: nil, + ConsensusState: consensusState, + InitialValSet: valUpdates, + }, + HeightToValsetUpdateId: nil, + OutstandingDowntimeSlashing: nil, + PendingConsumerPackets: types.ConsumerPacketDataList{}, + LastTransmissionBlockHeight: types.LastTransmissionBlockHeight{}, + PreCCV: false, + ConnectionId: "connection-1", + }, + true, + }, + { + "valid new consumer genesis state: connection id", + &types.GenesisState{ + Params: params, + ProviderClientId: "", + ProviderChannelId: "", + NewChain: true, + Provider: ccv.ProviderInfo{ + ClientState: nil, + ConsensusState: nil, + InitialValSet: valUpdates, + }, + HeightToValsetUpdateId: nil, + OutstandingDowntimeSlashing: nil, + PendingConsumerPackets: types.ConsumerPacketDataList{}, + LastTransmissionBlockHeight: types.LastTransmissionBlockHeight{}, + PreCCV: false, + ConnectionId: "connection-1", + }, + false, + }, } for _, c := range cases { diff --git a/x/ccv/provider/client/cli/tx.go b/x/ccv/provider/client/cli/tx.go index 1974a46b8c..36b1b1360d 100644 --- a/x/ccv/provider/client/cli/tx.go +++ b/x/ccv/provider/client/cli/tx.go @@ -245,7 +245,8 @@ where create_consumer.json has the following structure: "consumer_redistribution_fraction": "0.75", "blocks_per_distribution_transmission": 1000, "historical_entries": 10000, - "distribution_transmission_channel": "" + "distribution_transmission_channel": "", + "connection_id": "" }, "power_shaping_parameters": { "top_N": 0, @@ -356,7 +357,8 @@ where update_consumer.json has the following structure: "consumer_redistribution_fraction": "0.75", "blocks_per_distribution_transmission": 1000, "historical_entries": 10000, - "distribution_transmission_channel": "" + "distribution_transmission_channel": "", + "connection_id": "" }, "power_shaping_parameters": { "top_N": 0, diff --git a/x/ccv/provider/keeper/consumer_lifecycle.go b/x/ccv/provider/keeper/consumer_lifecycle.go index 4ea4503fce..2f6b47fde4 100644 --- a/x/ccv/provider/keeper/consumer_lifecycle.go +++ b/x/ccv/provider/keeper/consumer_lifecycle.go @@ -5,8 +5,10 @@ import ( "time" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + conntypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" + ibchost "github.com/cosmos/ibc-go/v8/modules/core/exported" ibctmtypes "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" errorsmod "cosmossdk.io/errors" @@ -290,6 +292,11 @@ func (k Keeper) CreateConsumerClient( if err != nil { return err } + if initializationRecord.ConnectionId != "" { + // there is no need to create a client if the connection ID is provided + // as the CCV channel will be built on top of the existing client + return nil + } phase := k.GetConsumerPhase(ctx, consumerId) if phase != types.CONSUMER_PHASE_INITIALIZED { @@ -385,39 +392,105 @@ func (k Keeper) MakeConsumerGenesis( consumerId, ) - // create provider client state and consensus state for the consumer to be able - // to create a provider client + var clientState *ibctmtypes.ClientState = nil + var tmConsState *ibctmtypes.ConsensusState = nil + var preCCV bool + var counterpartyConnectionId string - providerUnbondingPeriod, err := k.stakingKeeper.UnbondingTime(ctx) - if err != nil { - return gen, errorsmod.Wrapf(types.ErrNoUnbondingTime, "unbonding time not found: %s", err) - } - height := clienttypes.GetSelfHeight(ctx) + if initializationRecord.ConnectionId == "" { + // no connection ID provided + preCCV = false + counterpartyConnectionId = "" - clientState := k.GetTemplateClient(ctx) - // this is the counter party chain ID for the consumer - clientState.ChainId = ctx.ChainID() - // this is the latest height the client was updated at, i.e., - // the height of the latest consensus state (see below) - clientState.LatestHeight = height - trustPeriod, err := ccv.CalculateTrustPeriod(providerUnbondingPeriod, k.GetTrustingPeriodFraction(ctx)) - if err != nil { - return gen, errorsmod.Wrapf(sdkerrors.ErrInvalidHeight, "error %s calculating trusting_period for: %s", err, height) - } - clientState.TrustingPeriod = trustPeriod - clientState.UnbondingPeriod = providerUnbondingPeriod + // create provider client state and consensus state for the consumer to be able + // to create a provider client - consState, err := k.clientKeeper.GetSelfConsensusState(ctx, height) - if err != nil { - return gen, errorsmod.Wrapf(clienttypes.ErrConsensusStateNotFound, "error %s getting self consensus state for: %s", err, height) + providerUnbondingPeriod, err := k.stakingKeeper.UnbondingTime(ctx) + if err != nil { + return gen, errorsmod.Wrapf(types.ErrNoUnbondingTime, "unbonding time not found: %s", err) + } + height := clienttypes.GetSelfHeight(ctx) + + clientState = k.GetTemplateClient(ctx) + // this is the counter party chain ID for the consumer + clientState.ChainId = ctx.ChainID() + // this is the latest height the client was updated at, i.e., + // the height of the latest consensus state (see below) + clientState.LatestHeight = height + trustPeriod, err := ccv.CalculateTrustPeriod(providerUnbondingPeriod, k.GetTrustingPeriodFraction(ctx)) + if err != nil { + return gen, errorsmod.Wrapf(sdkerrors.ErrInvalidHeight, "error %s calculating trusting_period for: %s", err, height) + } + clientState.TrustingPeriod = trustPeriod + clientState.UnbondingPeriod = providerUnbondingPeriod + + consState, err := k.clientKeeper.GetSelfConsensusState(ctx, height) + if err != nil { + return gen, errorsmod.Wrapf(clienttypes.ErrConsensusStateNotFound, "error %s getting self consensus state for: %s", err, height) + } + tmConsState = consState.(*ibctmtypes.ConsensusState) + } else { + // connection ID provided + preCCV = true + + // get the connection end + connectionEnd, found := k.connectionKeeper.GetConnection(ctx, initializationRecord.ConnectionId) + if !found { + return gen, errorsmod.Wrapf(conntypes.ErrConnectionNotFound, + "could not find connection(%s)", initializationRecord.ConnectionId) + } + clientId := connectionEnd.ClientId + + // check that the underlying client is a Tendermint client and that the chain ID matches + clientState, found := k.clientKeeper.GetClientState(ctx, clientId) + if !found { + return gen, errorsmod.Wrapf(clienttypes.ErrClientNotFound, + "could not find client(%s) associated with connection(%s)", + clientId, initializationRecord.ConnectionId, + ) + } + tmClient, ok := clientState.(*ibctmtypes.ClientState) + if !ok { + return gen, errorsmod.Wrapf(clienttypes.ErrInvalidClientType, + "invalid client type. expected %s, got %s", + ibchost.Tendermint, clientState.ClientType(), + ) + } + consumerChainId, err := k.GetConsumerChainId(ctx, consumerId) + if err != nil { + return gen, err + } + if tmClient.ChainId != consumerChainId { + return gen, errorsmod.Wrapf(conntypes.ErrInvalidConnectionIdentifier, + "invalid connection(%s): expected chain ID %s, got %s", + initializationRecord.ConnectionId, consumerChainId, tmClient.ChainId, + ) + } + + // set the counterparty connection ID + counterpartyConnectionId = connectionEnd.Counterparty.ConnectionId + + k.SetConsumerClientId(ctx, consumerId, clientId) + + // Set minimum height for equivocation evidence from this consumer chain + k.SetEquivocationEvidenceMinHeight(ctx, consumerId, tmClient.LatestHeight.RevisionHeight) + + k.Logger(ctx).Info("use existing client and connection for consumer chain", + "consumer id", consumerId, + "client id", clientId, + "connection id", initializationRecord.ConnectionId, + ) } gen = *ccv.NewInitialConsumerGenesisState( clientState, - consState.(*ibctmtypes.ConsensusState), + tmConsState, initialValidatorUpdates, + preCCV, + counterpartyConnectionId, consumerGenesisParams, ) + return gen, nil } diff --git a/x/ccv/provider/types/genesis.go b/x/ccv/provider/types/genesis.go index daefd8785e..91fa538a26 100644 --- a/x/ccv/provider/types/genesis.go +++ b/x/ccv/provider/types/genesis.go @@ -82,10 +82,6 @@ func (cs ConsumerState) Validate() error { if err := host.ClientIdentifierValidator(cs.ClientId); err != nil { return err } - // consumer genesis should be for a new chain only - if !cs.ConsumerGenesis.NewChain { - return errors.New("consumer genesis must be for a new chain") - } // validate a new chain genesis if err := cs.ConsumerGenesis.Validate(); err != nil { return err diff --git a/x/ccv/provider/types/genesis_test.go b/x/ccv/provider/types/genesis_test.go index 1539d1a5fe..4fdbd7c26f 100644 --- a/x/ccv/provider/types/genesis_test.go +++ b/x/ccv/provider/types/genesis_test.go @@ -33,7 +33,7 @@ func TestValidateGenesisState(t *testing.T) { types.NewGenesisState( types.DefaultValsetUpdateID, nil, - []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-1")}}, + []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-1", false)}}, types.DefaultParams(), nil, nil, @@ -47,10 +47,10 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultValsetUpdateID, nil, []types.ConsumerState{ - {ChainId: "chainid-1", ChannelId: "channelid1", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-1")}, - {ChainId: "chainid-2", ChannelId: "channelid2", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-2")}, - {ChainId: "chainid-3", ChannelId: "channelid3", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-3")}, - {ChainId: "chainid-4", ChannelId: "channelid4", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-4")}, + {ChainId: "chainid-1", ChannelId: "channelid1", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-1", false)}, + {ChainId: "chainid-2", ChannelId: "channelid2", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-2", true)}, + {ChainId: "chainid-3", ChannelId: "channelid3", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-3", false)}, + {ChainId: "chainid-4", ChannelId: "channelid4", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-4", true)}, }, types.DefaultParams(), nil, @@ -64,9 +64,8 @@ func TestValidateGenesisState(t *testing.T) { types.NewGenesisState( types.DefaultValsetUpdateID, nil, - []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-1")}}, - types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, - time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), + []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-1", false)}}, + types.NewParams(types.DefaultTemplateClient(), types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24, 180), nil, nil, @@ -80,9 +79,7 @@ func TestValidateGenesisState(t *testing.T) { 0, 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, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24, 180), + types.DefaultParams(), nil, nil, nil, @@ -95,28 +92,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultValsetUpdateID, []types.ValsetUpdateIdToHeight{{ValsetUpdateId: 0}}, 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, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24, 180), - nil, - nil, - nil, - ), - false, - }, - { - "invalid params", - types.NewGenesisState( - types.DefaultValsetUpdateID, - nil, - []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id"}}, - types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, - 0, clienttypes.Height{}, nil, []string{"ibc", "upgradedIBCState"}), - types.DefaultTrustingPeriodFraction, - ccv.DefaultCCVTimeoutPeriod, - types.DefaultSlashMeterReplenishPeriod, - types.DefaultSlashMeterReplenishFraction, - sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 600, 24, 180), + types.DefaultParams(), nil, nil, nil, @@ -129,8 +105,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultValsetUpdateID, nil, []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id"}}, - types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, - time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), + types.NewParams(types.DefaultTemplateClient(), "0.0", // 0 trusting period fraction here ccv.DefaultCCVTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, @@ -148,8 +123,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultValsetUpdateID, nil, []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id"}}, - types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, - time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), + types.NewParams(types.DefaultTemplateClient(), types.DefaultTrustingPeriodFraction, 0, // 0 ccv timeout here types.DefaultSlashMeterReplenishPeriod, @@ -167,8 +141,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultValsetUpdateID, nil, []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id"}}, - types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, - time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), + types.NewParams(types.DefaultTemplateClient(), types.DefaultTrustingPeriodFraction, ccv.DefaultCCVTimeoutPeriod, 0, // 0 slash meter replenish period here @@ -186,8 +159,7 @@ func TestValidateGenesisState(t *testing.T) { types.DefaultValsetUpdateID, nil, []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id"}}, - types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, - time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), + types.NewParams(types.DefaultTemplateClient(), types.DefaultTrustingPeriodFraction, ccv.DefaultCCVTimeoutPeriod, types.DefaultSlashMeterReplenishPeriod, @@ -199,25 +171,12 @@ func TestValidateGenesisState(t *testing.T) { ), false, }, - { - "invalid consumer state chain id", - types.NewGenesisState( - types.DefaultValsetUpdateID, - nil, - []types.ConsumerState{{ChainId: "", ChannelId: "channelid", ClientId: "client-id"}}, - types.DefaultParams(), - nil, - nil, - nil, - ), - false, - }, { "invalid consumer state channel id", types.NewGenesisState( types.DefaultValsetUpdateID, nil, - []types.ConsumerState{{ChainId: "chainid", ChannelId: "ivnalidChannel{}", ClientId: "client-id"}}, + []types.ConsumerState{{ChainId: "chainid", ChannelId: "invalidChannel{}", ClientId: "client-id"}}, types.DefaultParams(), nil, nil, @@ -243,23 +202,7 @@ func TestValidateGenesisState(t *testing.T) { types.NewGenesisState( types.DefaultValsetUpdateID, nil, - []types.ConsumerState{{ChainId: "chainid", ChannelId: "channel-0", ClientId: "abc", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid")}}, - types.DefaultParams(), - nil, - nil, - nil, - ), - false, - }, - { - "empty consumer state consumer genesis", - types.NewGenesisState( - types.DefaultValsetUpdateID, - nil, - []types.ConsumerState{{ - ChainId: "chainid", ChannelId: "channel-0", ClientId: "client-id", - ConsumerGenesis: ccv.ConsumerGenesisState{}, - }}, + []types.ConsumerState{{ChainId: "chainid", ChannelId: "channel-0", ClientId: "abc", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid", false)}}, types.DefaultParams(), nil, nil, @@ -274,7 +217,7 @@ func TestValidateGenesisState(t *testing.T) { nil, []types.ConsumerState{{ ChainId: "chainid", ChannelId: "channel-0", ClientId: "client-id", - ConsumerGenesis: getInitialConsumerGenesis(t, "chainid"), + ConsumerGenesis: getInitialConsumerGenesis(t, "chainid", false), SlashDowntimeAck: []string{"cosmosvaloper1qlmk6r5w5taqrky4ycur4zq6jqxmuzr688htpp"}, }}, types.DefaultParams(), @@ -291,7 +234,7 @@ func TestValidateGenesisState(t *testing.T) { nil, []types.ConsumerState{{ ChainId: "chainid", ChannelId: "channel-0", ClientId: "client-id", - ConsumerGenesis: getInitialConsumerGenesis(t, "chainid"), + ConsumerGenesis: getInitialConsumerGenesis(t, "chainid", false), PendingValsetChanges: []ccv.ValidatorSetChangePacketData{{}}, }}, types.DefaultParams(), @@ -308,7 +251,7 @@ func TestValidateGenesisState(t *testing.T) { nil, []types.ConsumerState{{ ChainId: "chainid", ChannelId: "channel-0", ClientId: "client-id", - ConsumerGenesis: getInitialConsumerGenesis(t, "chainid"), + ConsumerGenesis: getInitialConsumerGenesis(t, "chainid", false), PendingValsetChanges: []ccv.ValidatorSetChangePacketData{{ SlashAcks: []string{"cosmosvaloper1qlmk6r5w5taqrky4ycur4zq6jqxmuzr688htpp"}, ValsetUpdateId: 1, @@ -327,9 +270,8 @@ func TestValidateGenesisState(t *testing.T) { types.NewGenesisState( types.DefaultValsetUpdateID, nil, - []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-1")}}, - types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, - time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), + []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-1", false)}}, + types.NewParams(types.DefaultTemplateClient(), types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: math.NewInt(10000000)}, 600, 24, 180), nil, nil, @@ -342,9 +284,8 @@ func TestValidateGenesisState(t *testing.T) { types.NewGenesisState( types.DefaultValsetUpdateID, nil, - []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-1")}}, - types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, - time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), + []types.ConsumerState{{ChainId: "chainid-1", ChannelId: "channelid", ClientId: "client-id", ConsumerGenesis: getInitialConsumerGenesis(t, "chainid-1", false)}}, + types.NewParams(types.DefaultTemplateClient(), types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(-1000000)}, 600, 24, 180), nil, nil, @@ -367,7 +308,7 @@ func TestValidateGenesisState(t *testing.T) { } } -func getInitialConsumerGenesis(t *testing.T, chainID string) ccv.ConsumerGenesisState { +func getInitialConsumerGenesis(t *testing.T, chainID string, preCCV bool) ccv.ConsumerGenesisState { t.Helper() // generate validator public key cId := crypto.NewCryptoIdentityFromIntSeed(239668) @@ -379,18 +320,27 @@ func getInitialConsumerGenesis(t *testing.T, chainID string) ccv.ConsumerGenesis valHash := valSet.Hash() valUpdates := tmtypes.TM2PB.ValidatorUpdates(valSet) - cs := ibctmtypes.NewClientState( - chainID, - ibctmtypes.DefaultTrustLevel, - time.Duration(1), - time.Duration(2), - time.Duration(1), - clienttypes.Height{RevisionNumber: clienttypes.ParseChainID(chainID), RevisionHeight: 1}, - commitmenttypes.GetSDKSpecs(), - []string{"upgrade", "upgradedIBCState"}) - consensusState := ibctmtypes.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("apphash")), valHash) + var clientState *ibctmtypes.ClientState = nil + var consensusState *ibctmtypes.ConsensusState = nil + connectionId := "" + + if preCCV { + connectionId = "connection-1" + } else { + clientState = ibctmtypes.NewClientState( + chainID, + ibctmtypes.DefaultTrustLevel, + time.Duration(1), + time.Duration(2), + time.Duration(1), + clienttypes.Height{RevisionNumber: clienttypes.ParseChainID(chainID), RevisionHeight: 1}, + commitmenttypes.GetSDKSpecs(), + []string{"upgrade", "upgradedIBCState"}) + consensusState = ibctmtypes.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("apphash")), valHash) + } params := ccv.DefaultParams() params.Enabled = true - return *ccv.NewInitialConsumerGenesisState(cs, consensusState, valUpdates, params) + + return *ccv.NewInitialConsumerGenesisState(clientState, consensusState, valUpdates, preCCV, connectionId, params) } diff --git a/x/ccv/provider/types/msg.go b/x/ccv/provider/types/msg.go index 0264236c66..bf52a5b2a7 100644 --- a/x/ccv/provider/types/msg.go +++ b/x/ccv/provider/types/msg.go @@ -623,6 +623,10 @@ func ValidateInitializationParameters(initializationParameters ConsumerInitializ return errorsmod.Wrapf(ErrInvalidConsumerInitializationParameters, "UnbondingPeriod: %s", err.Error()) } + if err := ccvtypes.ValidateConnectionIdentifier(initializationParameters.ConnectionId); err != nil { + return errorsmod.Wrapf(ErrInvalidConsumerInitializationParameters, "ConnectionId: %s", err.Error()) + } + return nil } diff --git a/x/ccv/provider/types/msg_test.go b/x/ccv/provider/types/msg_test.go index 8fe82b9ed0..fcfb1e94d1 100644 --- a/x/ccv/provider/types/msg_test.go +++ b/x/ccv/provider/types/msg_test.go @@ -168,6 +168,7 @@ func TestValidateInitializationParameters(t *testing.T) { BlocksPerDistributionTransmission: 10, HistoricalEntries: 10000, DistributionTransmissionChannel: "", + ConnectionId: "", }, valid: true, }, @@ -185,6 +186,7 @@ func TestValidateInitializationParameters(t *testing.T) { BlocksPerDistributionTransmission: 10, HistoricalEntries: 10000, DistributionTransmissionChannel: "", + ConnectionId: "", }, valid: false, }, @@ -202,6 +204,7 @@ func TestValidateInitializationParameters(t *testing.T) { BlocksPerDistributionTransmission: 10, HistoricalEntries: 10000, DistributionTransmissionChannel: "", + ConnectionId: "", }, valid: false, }, @@ -219,6 +222,7 @@ func TestValidateInitializationParameters(t *testing.T) { BlocksPerDistributionTransmission: 10, HistoricalEntries: 10000, DistributionTransmissionChannel: "", + ConnectionId: "", }, valid: true, }, @@ -236,6 +240,7 @@ func TestValidateInitializationParameters(t *testing.T) { BlocksPerDistributionTransmission: 10, HistoricalEntries: 10000, DistributionTransmissionChannel: "", + ConnectionId: "", }, valid: false, }, @@ -253,6 +258,7 @@ func TestValidateInitializationParameters(t *testing.T) { BlocksPerDistributionTransmission: 10, HistoricalEntries: 10000, DistributionTransmissionChannel: "", + ConnectionId: "", }, valid: false, }, @@ -270,6 +276,7 @@ func TestValidateInitializationParameters(t *testing.T) { BlocksPerDistributionTransmission: 10, HistoricalEntries: 10000, DistributionTransmissionChannel: "", + ConnectionId: "", }, valid: false, }, @@ -287,6 +294,7 @@ func TestValidateInitializationParameters(t *testing.T) { BlocksPerDistributionTransmission: 0, HistoricalEntries: 10000, DistributionTransmissionChannel: "", + ConnectionId: "", }, valid: false, }, @@ -304,6 +312,7 @@ func TestValidateInitializationParameters(t *testing.T) { BlocksPerDistributionTransmission: 10, HistoricalEntries: 0, DistributionTransmissionChannel: "", + ConnectionId: "", }, valid: false, }, @@ -321,6 +330,25 @@ func TestValidateInitializationParameters(t *testing.T) { BlocksPerDistributionTransmission: 10, HistoricalEntries: 10000, DistributionTransmissionChannel: coolStr, + ConnectionId: "", + }, + valid: false, + }, + { + name: "invalid - ConnectionId too long", + params: types.ConsumerInitializationParameters{ + InitialHeight: clienttypes.NewHeight(3, 4), + GenesisHash: []byte{0x01}, + BinaryHash: []byte{0x01}, + SpawnTime: now, + UnbondingPeriod: time.Duration(100000000000), + CcvTimeoutPeriod: time.Duration(100000000000), + TransferTimeoutPeriod: time.Duration(100000000000), + ConsumerRedistributionFraction: "0.75", + BlocksPerDistributionTransmission: 10, + HistoricalEntries: 10000, + DistributionTransmissionChannel: "", + ConnectionId: coolStr, }, valid: false, }, diff --git a/x/ccv/provider/types/params.go b/x/ccv/provider/types/params.go index 2e144a5520..a278c81a73 100644 --- a/x/ccv/provider/types/params.go +++ b/x/ccv/provider/types/params.go @@ -99,21 +99,26 @@ func NewParams( } } +// DefaultTemplateClient is the default template client +func DefaultTemplateClient() *ibctmtypes.ClientState { + return ibctmtypes.NewClientState( + "", // chainID + ibctmtypes.DefaultTrustLevel, + 0, // trusting period + 0, // unbonding period + DefaultMaxClockDrift, + clienttypes.Height{}, // latest(initial) height + commitmenttypes.GetSDKSpecs(), + []string{"upgrade", "upgradedIBCState"}, + ) +} + // DefaultParams is the default params for the provider module func DefaultParams() Params { // create default client state with chainID, trusting period, unbonding period, and initial height zeroed out. // these fields will be populated during proposal handler. return NewParams( - ibctmtypes.NewClientState( - "", // chainID - ibctmtypes.DefaultTrustLevel, - 0, // trusting period - 0, // unbonding period - DefaultMaxClockDrift, - clienttypes.Height{}, // latest(initial) height - commitmenttypes.GetSDKSpecs(), - []string{"upgrade", "upgradedIBCState"}, - ), + DefaultTemplateClient(), DefaultTrustingPeriodFraction, ccvtypes.DefaultCCVTimeoutPeriod, DefaultSlashMeterReplenishPeriod, @@ -138,7 +143,7 @@ func (p Params) Validate() error { if err := ValidateTemplateClient(*p.TemplateClient); err != nil { return err } - if err := ccvtypes.ValidateStringFraction(p.TrustingPeriodFraction); err != nil { + if err := ccvtypes.ValidateStringFractionNonZero(p.TrustingPeriodFraction); err != nil { return fmt.Errorf("trusting period fraction is invalid: %s", err) } if err := ccvtypes.ValidateDuration(p.CcvTimeoutPeriod); err != nil { @@ -147,7 +152,7 @@ func (p Params) Validate() error { if err := ccvtypes.ValidateDuration(p.SlashMeterReplenishPeriod); err != nil { return fmt.Errorf("slash meter replenish period is invalid: %s", err) } - if err := ccvtypes.ValidateStringFraction(p.SlashMeterReplenishFraction); err != nil { + if err := ccvtypes.ValidateStringFractionNonZero(p.SlashMeterReplenishFraction); err != nil { return fmt.Errorf("slash meter replenish fraction is invalid: %s", err) } if err := ValidateCoin(p.ConsumerRewardDenomRegistrationFee); err != nil { diff --git a/x/ccv/provider/types/params_test.go b/x/ccv/provider/types/params_test.go index 29ece2f2c3..b323d31f26 100644 --- a/x/ccv/provider/types/params_test.go +++ b/x/ccv/provider/types/params_test.go @@ -34,10 +34,9 @@ func TestValidateParams(t *testing.T) { {"blank client", types.NewParams(&ibctmtypes.ClientState{}, "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, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24, 180), true}, + "0.00", time.Hour, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24, 180), false}, {"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, 30*time.Minute, "0.1", sdk.Coin{Denom: "stake", Amount: math.NewInt(10000000)}, 1000, 24, 180), false}, diff --git a/x/ccv/provider/types/provider.pb.go b/x/ccv/provider/types/provider.pb.go index ce65b5e111..ba60517a15 100644 --- a/x/ccv/provider/types/provider.pb.go +++ b/x/ccv/provider/types/provider.pb.go @@ -142,7 +142,7 @@ type ConsumerAdditionProposal struct { // 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 + // chain. It is most relevant for chains performing a standalone to consumer // changeover in order to maintain the existing ibc transfer channel DistributionTransmissionChannel string `protobuf:"bytes,14,opt,name=distribution_transmission_channel,json=distributionTransmissionChannel,proto3" json:"distribution_transmission_channel,omitempty"` // Corresponds to the percentage of validators that have to validate the chain under the Top N case. @@ -1538,9 +1538,15 @@ type ConsumerInitializationParameters struct { // 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 + // chain. It is most relevant for chains performing a standalone to consumer // changeover in order to maintain the existing ibc transfer channel DistributionTransmissionChannel string `protobuf:"bytes,11,opt,name=distribution_transmission_channel,json=distributionTransmissionChannel,proto3" json:"distribution_transmission_channel,omitempty"` + // The ID of the connection end on the provider chain on top of which the CCV + // channel will be established. If connection_id == "", a new client of the + // consumer chain and a new connection on top of this client are created. + // Note that a standalone chain can transition to a consumer chain while + // maintaining existing IBC channels to other chains by providing a valid connection_id. + ConnectionId string `protobuf:"bytes,12,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty"` } func (m *ConsumerInitializationParameters) Reset() { *m = ConsumerInitializationParameters{} } @@ -1653,6 +1659,13 @@ func (m *ConsumerInitializationParameters) GetDistributionTransmissionChannel() return "" } +func (m *ConsumerInitializationParameters) GetConnectionId() string { + if m != nil { + return m.ConnectionId + } + return "" +} + // PowerShapingParameters contains parameters that shape the validator set that we send to the consumer chain type PowerShapingParameters struct { // Corresponds to the percentage of validators that have to validate the chain under the Top N case. @@ -2029,159 +2042,160 @@ func init() { } var fileDescriptor_f22ec409a72b7b72 = []byte{ - // 2428 bytes of a gzipped FileDescriptorProto + // 2443 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x19, 0x4b, 0x6f, 0x1b, 0xc7, 0x59, 0x2b, 0x52, 0x12, 0x39, 0xd4, 0x83, 0x1a, 0x29, 0x32, 0x25, 0x2b, 0x24, 0xbd, 0x69, 0x02, 0x35, 0xae, 0xc9, 0x48, 0x01, 0x02, 0xc3, 0x6d, 0x10, 0x50, 0x24, 0x63, 0xd1, 0x0f, 0x99, 0x5d, 0xd2, 0x0a, 0xea, 0xa2, 0x58, 0x0c, 0x77, 0x47, 0xe4, 0x44, 0xbb, 0x3b, 0xeb, 0x9d, 0x21, 0x65, - 0xf6, 0xd0, 0x73, 0x50, 0xa0, 0x40, 0x7a, 0x0b, 0x7a, 0x69, 0x80, 0x5e, 0x8a, 0x9e, 0x7a, 0x08, - 0xf2, 0x03, 0x7a, 0x69, 0x5a, 0xa0, 0x40, 0xda, 0x53, 0x51, 0x14, 0x4e, 0x61, 0x1f, 0x7a, 0x28, - 0xd0, 0x9e, 0x7b, 0x2b, 0x66, 0xf6, 0xc1, 0xa5, 0x5e, 0xa6, 0x61, 0xbb, 0x17, 0x69, 0xe7, 0x7b, - 0xcd, 0xf7, 0xcd, 0x7c, 0xcf, 0x21, 0xd8, 0x21, 0x0e, 0xc7, 0x9e, 0xd1, 0x43, 0xc4, 0xd1, 0x19, - 0x36, 0xfa, 0x1e, 0xe1, 0xc3, 0xb2, 0x61, 0x0c, 0xca, 0xae, 0x47, 0x07, 0xc4, 0xc4, 0x5e, 0x79, - 0xb0, 0x1d, 0x7d, 0x97, 0x5c, 0x8f, 0x72, 0x0a, 0xdf, 0x38, 0x83, 0xa7, 0x64, 0x18, 0x83, 0x52, - 0x44, 0x37, 0xd8, 0xde, 0x58, 0x46, 0x36, 0x71, 0x68, 0x59, 0xfe, 0xf5, 0xf9, 0x36, 0xf2, 0x06, - 0x65, 0x36, 0x65, 0xe5, 0x0e, 0x62, 0xb8, 0x3c, 0xd8, 0xee, 0x60, 0x8e, 0xb6, 0xcb, 0x06, 0x25, - 0x4e, 0x80, 0x7f, 0x2b, 0xc0, 0x63, 0x21, 0xc4, 0x31, 0x46, 0x34, 0x21, 0x20, 0xa0, 0x5b, 0xf7, - 0xe9, 0x74, 0xb9, 0x2a, 0xfb, 0x8b, 0x00, 0xb5, 0xda, 0xa5, 0x5d, 0xea, 0xc3, 0xc5, 0x57, 0xb8, - 0x71, 0x97, 0xd2, 0xae, 0x85, 0xcb, 0x72, 0xd5, 0xe9, 0x1f, 0x96, 0xcd, 0xbe, 0x87, 0x38, 0xa1, - 0xe1, 0xc6, 0x85, 0x93, 0x78, 0x4e, 0x6c, 0xcc, 0x38, 0xb2, 0xdd, 0x90, 0x80, 0x74, 0x8c, 0xb2, - 0x41, 0x3d, 0x5c, 0x36, 0x2c, 0x82, 0x1d, 0x2e, 0x0e, 0xc5, 0xff, 0x0a, 0x08, 0xca, 0x82, 0xc0, - 0x22, 0xdd, 0x1e, 0xf7, 0xc1, 0xac, 0xcc, 0xb1, 0x63, 0x62, 0xcf, 0x26, 0x3e, 0xf1, 0x68, 0x15, - 0x30, 0xbc, 0x79, 0xde, 0xb9, 0x0f, 0xb6, 0xcb, 0xc7, 0xc4, 0x0b, 0x4d, 0xdd, 0x8c, 0x89, 0x31, - 0xbc, 0xa1, 0xcb, 0x69, 0xf9, 0x08, 0x0f, 0x03, 0x6b, 0xd5, 0xff, 0xa6, 0x40, 0xae, 0x4a, 0x1d, - 0xd6, 0xb7, 0xb1, 0x57, 0x31, 0x4d, 0x22, 0x4c, 0x6a, 0x7a, 0xd4, 0xa5, 0x0c, 0x59, 0x70, 0x15, - 0xcc, 0x70, 0xc2, 0x2d, 0x9c, 0x53, 0x8a, 0xca, 0x56, 0x5a, 0xf3, 0x17, 0xb0, 0x08, 0x32, 0x26, - 0x66, 0x86, 0x47, 0x5c, 0x41, 0x9c, 0x9b, 0x96, 0xb8, 0x38, 0x08, 0xae, 0x83, 0x94, 0xaf, 0x16, - 0x31, 0x73, 0x09, 0x89, 0x9e, 0x93, 0xeb, 0x86, 0x09, 0x6f, 0x82, 0x45, 0xe2, 0x10, 0x4e, 0x90, - 0xa5, 0xf7, 0xb0, 0x30, 0x36, 0x97, 0x2c, 0x2a, 0x5b, 0x99, 0x9d, 0x8d, 0x12, 0xe9, 0x18, 0x25, - 0x71, 0x3e, 0xa5, 0xe0, 0x54, 0x06, 0xdb, 0xa5, 0x3d, 0x49, 0xb1, 0x9b, 0xfc, 0xea, 0x71, 0x61, - 0x4a, 0x5b, 0x08, 0xf8, 0x7c, 0x20, 0xbc, 0x02, 0xe6, 0xbb, 0xd8, 0xc1, 0x8c, 0x30, 0xbd, 0x87, - 0x58, 0x2f, 0x37, 0x53, 0x54, 0xb6, 0xe6, 0xb5, 0x4c, 0x00, 0xdb, 0x43, 0xac, 0x07, 0x0b, 0x20, - 0xd3, 0x21, 0x0e, 0xf2, 0x86, 0x3e, 0xc5, 0xac, 0xa4, 0x00, 0x3e, 0x48, 0x12, 0x54, 0x01, 0x60, - 0x2e, 0x3a, 0x76, 0x74, 0x71, 0x59, 0xb9, 0xb9, 0x40, 0x11, 0xff, 0x26, 0x4b, 0xe1, 0x4d, 0x96, - 0xda, 0xe1, 0x4d, 0xee, 0xa6, 0x84, 0x22, 0x9f, 0x7e, 0x53, 0x50, 0xb4, 0xb4, 0xe4, 0x13, 0x18, - 0xb8, 0x0f, 0xb2, 0x7d, 0xa7, 0x43, 0x1d, 0x93, 0x38, 0x5d, 0xdd, 0xc5, 0x1e, 0xa1, 0x66, 0x2e, - 0x25, 0x45, 0xad, 0x9f, 0x12, 0x55, 0x0b, 0x9c, 0xc6, 0x97, 0xf4, 0x99, 0x90, 0xb4, 0x14, 0x31, - 0x37, 0x25, 0x2f, 0xfc, 0x3e, 0x80, 0x86, 0x31, 0x90, 0x2a, 0xd1, 0x3e, 0x0f, 0x25, 0xa6, 0x27, - 0x97, 0x98, 0x35, 0x8c, 0x41, 0xdb, 0xe7, 0x0e, 0x44, 0xfe, 0x10, 0x5c, 0xe2, 0x1e, 0x72, 0xd8, - 0x21, 0xf6, 0x4e, 0xca, 0x05, 0x93, 0xcb, 0x7d, 0x2d, 0x94, 0x31, 0x2e, 0x7c, 0x0f, 0x14, 0x8d, - 0xc0, 0x81, 0x74, 0x0f, 0x9b, 0x84, 0x71, 0x8f, 0x74, 0xfa, 0x82, 0x57, 0x3f, 0xf4, 0x90, 0x21, - 0x7d, 0x24, 0x23, 0x9d, 0x20, 0x1f, 0xd2, 0x69, 0x63, 0x64, 0x1f, 0x06, 0x54, 0xf0, 0x1e, 0xf8, - 0x56, 0xc7, 0xa2, 0xc6, 0x11, 0x13, 0xca, 0xe9, 0x63, 0x92, 0xe4, 0xd6, 0x36, 0x61, 0x4c, 0x48, - 0x9b, 0x2f, 0x2a, 0x5b, 0x09, 0xed, 0x8a, 0x4f, 0xdb, 0xc4, 0x5e, 0x2d, 0x46, 0xd9, 0x8e, 0x11, - 0xc2, 0x6b, 0x00, 0xf6, 0x08, 0xe3, 0xd4, 0x23, 0x06, 0xb2, 0x74, 0xec, 0x70, 0x8f, 0x60, 0x96, - 0x5b, 0x90, 0xec, 0xcb, 0x23, 0x4c, 0xdd, 0x47, 0xc0, 0x5b, 0xe0, 0xca, 0xb9, 0x9b, 0xea, 0x46, - 0x0f, 0x39, 0x0e, 0xb6, 0x72, 0x8b, 0xd2, 0x94, 0x82, 0x79, 0xce, 0x9e, 0x55, 0x9f, 0x0c, 0xae, - 0x80, 0x19, 0x4e, 0x5d, 0x7d, 0x3f, 0xb7, 0x54, 0x54, 0xb6, 0x16, 0xb4, 0x24, 0xa7, 0xee, 0x3e, - 0x7c, 0x07, 0xac, 0x0e, 0x90, 0x45, 0x4c, 0xc4, 0xa9, 0xc7, 0x74, 0x97, 0x1e, 0x63, 0x4f, 0x37, - 0x90, 0x9b, 0xcb, 0x4a, 0x1a, 0x38, 0xc2, 0x35, 0x05, 0xaa, 0x8a, 0x5c, 0xf8, 0x36, 0x58, 0x8e, - 0xa0, 0x3a, 0xc3, 0x5c, 0x92, 0x2f, 0x4b, 0xf2, 0xa5, 0x08, 0xd1, 0xc2, 0x5c, 0xd0, 0x6e, 0x82, - 0x34, 0xb2, 0x2c, 0x7a, 0x6c, 0x11, 0xc6, 0x73, 0xb0, 0x98, 0xd8, 0x4a, 0x6b, 0x23, 0x00, 0xdc, - 0x00, 0x29, 0x13, 0x3b, 0x43, 0x89, 0x5c, 0x91, 0xc8, 0x68, 0x0d, 0x2f, 0x83, 0xb4, 0x2d, 0x92, - 0x08, 0x47, 0x47, 0x38, 0xb7, 0x5a, 0x54, 0xb6, 0x92, 0x5a, 0xca, 0x26, 0x4e, 0x4b, 0xac, 0x61, - 0x09, 0xac, 0x48, 0x29, 0x3a, 0x71, 0xc4, 0x3d, 0x0d, 0xb0, 0x3e, 0x40, 0x16, 0xcb, 0xbd, 0x56, - 0x54, 0xb6, 0x52, 0xda, 0xb2, 0x44, 0x35, 0x02, 0xcc, 0x01, 0xb2, 0xd8, 0x8d, 0xad, 0x4f, 0x3e, - 0x2f, 0x4c, 0x7d, 0xf6, 0x79, 0x61, 0xea, 0x8f, 0x5f, 0x5c, 0xdb, 0x08, 0x32, 0x6b, 0x97, 0x0e, - 0x4a, 0x41, 0x26, 0x2e, 0x55, 0xa9, 0xc3, 0xb1, 0xc3, 0x73, 0x8a, 0xfa, 0x67, 0x05, 0x5c, 0xaa, - 0x46, 0x2e, 0x61, 0xd3, 0x01, 0xb2, 0x5e, 0x65, 0xea, 0xa9, 0x80, 0x34, 0x13, 0x77, 0x22, 0x83, - 0x3d, 0xf9, 0x1c, 0xc1, 0x9e, 0x12, 0x6c, 0x02, 0x71, 0xa3, 0xf8, 0x4c, 0x9b, 0xfe, 0x33, 0x0d, - 0x36, 0x43, 0x9b, 0xee, 0x52, 0x93, 0x1c, 0x12, 0x03, 0xbd, 0xea, 0x9c, 0x1a, 0xf9, 0x5a, 0x72, - 0x02, 0x5f, 0x9b, 0x79, 0x3e, 0x5f, 0x9b, 0x9d, 0xc0, 0xd7, 0xe6, 0x2e, 0xf2, 0xb5, 0xd4, 0x45, - 0xbe, 0x96, 0x9e, 0xcc, 0xd7, 0xc0, 0x79, 0xbe, 0x36, 0x9d, 0x53, 0xd4, 0x5f, 0x2a, 0x60, 0xb5, - 0xfe, 0xb0, 0x4f, 0x06, 0xf4, 0x25, 0x9d, 0xf4, 0x6d, 0xb0, 0x80, 0x63, 0xf2, 0x58, 0x2e, 0x51, - 0x4c, 0x6c, 0x65, 0x76, 0xde, 0x2c, 0x05, 0x17, 0x1f, 0xb5, 0x12, 0xe1, 0xed, 0xc7, 0x77, 0xd7, - 0xc6, 0x79, 0xa5, 0x86, 0xbf, 0x53, 0xc0, 0x86, 0xc8, 0x0b, 0x5d, 0xac, 0xe1, 0x63, 0xe4, 0x99, - 0x35, 0xec, 0x50, 0x9b, 0xbd, 0xb0, 0x9e, 0x2a, 0x58, 0x30, 0xa5, 0x24, 0x9d, 0x53, 0x1d, 0x99, - 0xa6, 0xd4, 0x53, 0xd2, 0x08, 0x60, 0x9b, 0x56, 0x4c, 0x13, 0x6e, 0x81, 0xec, 0x88, 0xc6, 0x13, - 0x31, 0x26, 0x5c, 0x5f, 0x90, 0x2d, 0x86, 0x64, 0x32, 0xf2, 0xf0, 0x8d, 0xfc, 0xc5, 0xae, 0xad, - 0xfe, 0x4b, 0x01, 0xd9, 0x9b, 0x16, 0xed, 0x20, 0xab, 0x65, 0x21, 0xd6, 0x13, 0x39, 0x73, 0x28, - 0x42, 0xca, 0xc3, 0x41, 0xb1, 0x92, 0xea, 0x4f, 0x1c, 0x52, 0x82, 0x4d, 0x96, 0xcf, 0x0f, 0xc0, - 0x72, 0x54, 0x3e, 0x22, 0x07, 0x97, 0xd6, 0xee, 0xae, 0x3c, 0x79, 0x5c, 0x58, 0x0a, 0x83, 0xa9, - 0x2a, 0x9d, 0xbd, 0xa6, 0x2d, 0x19, 0x63, 0x00, 0x13, 0xe6, 0x41, 0x86, 0x74, 0x0c, 0x9d, 0xe1, - 0x87, 0xba, 0xd3, 0xb7, 0x65, 0x6c, 0x24, 0xb5, 0x34, 0xe9, 0x18, 0x2d, 0xfc, 0x70, 0xbf, 0x6f, - 0xc3, 0x77, 0xc1, 0x5a, 0xd8, 0x54, 0x0a, 0x6f, 0xd2, 0x05, 0xbf, 0x38, 0x2e, 0x4f, 0x86, 0xcb, - 0xbc, 0xb6, 0x12, 0x62, 0x0f, 0x90, 0x25, 0x36, 0xab, 0x98, 0xa6, 0xa7, 0xfe, 0x7b, 0x06, 0xcc, - 0x36, 0x91, 0x87, 0x6c, 0x06, 0xdb, 0x60, 0x89, 0x63, 0xdb, 0xb5, 0x10, 0xc7, 0xba, 0xdf, 0x9a, - 0x04, 0x96, 0x5e, 0x95, 0x2d, 0x4b, 0xbc, 0x63, 0x2b, 0xc5, 0x7a, 0xb4, 0xc1, 0x76, 0xa9, 0x2a, - 0xa1, 0x2d, 0x8e, 0x38, 0xd6, 0x16, 0x43, 0x19, 0x3e, 0x10, 0x5e, 0x07, 0x39, 0xee, 0xf5, 0x19, - 0x1f, 0x35, 0x0d, 0xa3, 0x6a, 0xe9, 0xdf, 0xf5, 0x5a, 0x88, 0xf7, 0xeb, 0x6c, 0x54, 0x25, 0xcf, - 0xee, 0x0f, 0x12, 0x2f, 0xd2, 0x1f, 0x98, 0x60, 0x93, 0x89, 0x4b, 0xd5, 0x6d, 0xcc, 0x65, 0x15, - 0x77, 0x2d, 0xec, 0x10, 0xd6, 0x0b, 0x85, 0xcf, 0x4e, 0x2e, 0x7c, 0x5d, 0x0a, 0xba, 0x2b, 0xe4, - 0x68, 0xa1, 0x98, 0x60, 0x97, 0x2a, 0xc8, 0x9f, 0xbd, 0x4b, 0x64, 0xf8, 0x9c, 0x34, 0xfc, 0xf2, - 0x19, 0x22, 0x22, 0xeb, 0x19, 0x78, 0x2b, 0xd6, 0x6d, 0x88, 0x68, 0xd2, 0xa5, 0x23, 0xeb, 0x1e, - 0xee, 0x8a, 0x92, 0x8c, 0xfc, 0xc6, 0x03, 0xe3, 0xa8, 0x63, 0x0a, 0x7c, 0x5a, 0x4c, 0x0c, 0x31, - 0xa7, 0x26, 0x4e, 0xd0, 0x56, 0xaa, 0xa3, 0xa6, 0x24, 0x8a, 0x4d, 0x2d, 0x26, 0xeb, 0x43, 0x8c, - 0x45, 0x14, 0xc5, 0x1a, 0x13, 0xec, 0x52, 0xa3, 0x27, 0x73, 0x52, 0x42, 0x5b, 0x8c, 0x9a, 0x90, - 0xba, 0x80, 0xc2, 0x07, 0xe0, 0xaa, 0xd3, 0xb7, 0x3b, 0xd8, 0xd3, 0xe9, 0xa1, 0x4f, 0x28, 0x23, - 0x8f, 0x71, 0xe4, 0x71, 0xdd, 0xc3, 0x06, 0x26, 0x03, 0x71, 0xe3, 0xbe, 0xe6, 0x4c, 0xf6, 0x45, - 0x09, 0xed, 0x4d, 0x9f, 0xe5, 0xde, 0xa1, 0x94, 0xc1, 0xda, 0xb4, 0x25, 0xc8, 0xb5, 0x90, 0xda, - 0x57, 0x8c, 0xc1, 0x06, 0xb8, 0x62, 0xa3, 0x47, 0x7a, 0xe4, 0xcc, 0x42, 0x71, 0xec, 0xb0, 0x3e, - 0xd3, 0x47, 0xc9, 0x3c, 0xe8, 0x8d, 0xf2, 0x36, 0x7a, 0xd4, 0x0c, 0xe8, 0xaa, 0x21, 0xd9, 0x41, - 0x44, 0x75, 0x2b, 0x99, 0x4a, 0x66, 0x67, 0x6e, 0x25, 0x53, 0x33, 0xd9, 0xd9, 0x5b, 0xc9, 0x54, - 0x2a, 0x9b, 0x56, 0xbf, 0x0d, 0xd2, 0x32, 0xae, 0x2b, 0xc6, 0x11, 0x93, 0xd9, 0xdd, 0x34, 0x3d, - 0xcc, 0x18, 0x66, 0x39, 0x25, 0xc8, 0xee, 0x21, 0x40, 0xe5, 0x60, 0xfd, 0xbc, 0x89, 0x81, 0xc1, - 0x8f, 0xc0, 0x9c, 0x8b, 0x65, 0x3b, 0x2b, 0x19, 0x33, 0x3b, 0xef, 0x97, 0x26, 0x18, 0xf5, 0x4a, - 0xe7, 0x09, 0xd4, 0x42, 0x69, 0xaa, 0x37, 0x9a, 0x53, 0x4e, 0xf4, 0x0a, 0x0c, 0x1e, 0x9c, 0xdc, - 0xf4, 0x7b, 0xcf, 0xb5, 0xe9, 0x09, 0x79, 0xa3, 0x3d, 0xaf, 0x82, 0x4c, 0xc5, 0x37, 0xfb, 0x8e, - 0x28, 0x5d, 0xa7, 0x8e, 0x65, 0x3e, 0x7e, 0x2c, 0xfb, 0x60, 0x31, 0x68, 0xfe, 0xda, 0x54, 0xe6, - 0x26, 0xf8, 0x3a, 0x00, 0x41, 0xd7, 0x28, 0x72, 0x9a, 0x9f, 0xdd, 0xd3, 0x01, 0xa4, 0x61, 0x8e, - 0x55, 0xf4, 0xe9, 0xb1, 0x8a, 0x2e, 0xab, 0x06, 0x05, 0xeb, 0x07, 0xf1, 0xaa, 0x2b, 0x0b, 0x48, - 0x13, 0x19, 0x47, 0x98, 0x33, 0xa8, 0x81, 0xa4, 0xac, 0xae, 0xbe, 0xb9, 0xd7, 0xcf, 0x35, 0x77, - 0xb0, 0x5d, 0x3a, 0x4f, 0x48, 0x0d, 0x71, 0x14, 0xc4, 0x80, 0x94, 0xa5, 0xfe, 0x5c, 0x01, 0xb9, - 0xdb, 0x78, 0x58, 0x61, 0x8c, 0x74, 0x1d, 0x1b, 0x3b, 0x5c, 0x44, 0x1f, 0x32, 0xb0, 0xf8, 0x84, - 0x6f, 0x80, 0x85, 0xc8, 0xf1, 0x64, 0xf2, 0x54, 0x64, 0xf2, 0x9c, 0x0f, 0x81, 0xe2, 0x9c, 0xe0, - 0x0d, 0x00, 0x5c, 0x0f, 0x0f, 0x74, 0x43, 0x3f, 0xc2, 0x43, 0x69, 0x53, 0x66, 0x67, 0x33, 0x9e, - 0x14, 0xfd, 0xf9, 0xb3, 0xd4, 0xec, 0x77, 0x2c, 0x62, 0xdc, 0xc6, 0x43, 0x2d, 0x25, 0xe8, 0xab, - 0xb7, 0xf1, 0x50, 0x54, 0x41, 0xd9, 0xa4, 0xc8, 0x4c, 0x96, 0xd0, 0xfc, 0x85, 0xfa, 0x0b, 0x05, - 0x5c, 0x8a, 0x0c, 0x08, 0xef, 0xab, 0xd9, 0xef, 0x08, 0x8e, 0xf8, 0xf9, 0x29, 0xe3, 0x1d, 0xd1, - 0x29, 0x6d, 0xa7, 0xcf, 0xd0, 0xf6, 0x03, 0x30, 0x1f, 0xa5, 0x12, 0xa1, 0x6f, 0x62, 0x02, 0x7d, - 0x33, 0x21, 0xc7, 0x6d, 0x3c, 0x54, 0x7f, 0x12, 0xd3, 0x6d, 0x77, 0x18, 0x73, 0x61, 0xef, 0x19, - 0xba, 0x45, 0xdb, 0xc6, 0x75, 0x33, 0xe2, 0xfc, 0xa7, 0x0c, 0x48, 0x9c, 0x36, 0x40, 0xfd, 0x93, - 0x02, 0xd6, 0xe2, 0xbb, 0xb2, 0x36, 0x6d, 0x7a, 0x7d, 0x07, 0x1f, 0xec, 0x5c, 0xb4, 0xff, 0x07, - 0x20, 0xe5, 0x0a, 0x2a, 0x9d, 0xb3, 0xe0, 0x8a, 0x26, 0x2b, 0xd9, 0x73, 0x92, 0xab, 0x2d, 0x42, - 0x7c, 0x71, 0xcc, 0x00, 0x16, 0x9c, 0xdc, 0x3b, 0x13, 0x05, 0x5d, 0x2c, 0xa0, 0xb4, 0x85, 0xb8, - 0xcd, 0x4c, 0xfd, 0x52, 0x01, 0xf0, 0x74, 0xb6, 0x82, 0xdf, 0x01, 0x70, 0x2c, 0xe7, 0xc5, 0xfd, - 0x2f, 0xeb, 0xc6, 0xb2, 0x9c, 0x3c, 0xb9, 0xc8, 0x8f, 0xa6, 0x63, 0x7e, 0x04, 0xbf, 0x0b, 0x80, - 0x2b, 0x2f, 0x71, 0xe2, 0x9b, 0x4e, 0xbb, 0xe1, 0x27, 0x2c, 0x80, 0xcc, 0xc7, 0x94, 0x38, 0xf1, - 0x07, 0x8b, 0x84, 0x06, 0x04, 0xc8, 0x7f, 0x8b, 0x50, 0x7f, 0xa6, 0x8c, 0x52, 0x62, 0x90, 0xad, - 0x2b, 0x96, 0x15, 0xf4, 0x80, 0xd0, 0x05, 0x73, 0x61, 0xbe, 0xf7, 0xc3, 0x75, 0xf3, 0xcc, 0x9a, - 0x54, 0xc3, 0x86, 0x2c, 0x4b, 0xd7, 0xc5, 0x89, 0xff, 0xe6, 0x9b, 0xc2, 0xd5, 0x2e, 0xe1, 0xbd, - 0x7e, 0xa7, 0x64, 0x50, 0x3b, 0x78, 0xa0, 0x0a, 0xfe, 0x5d, 0x63, 0xe6, 0x51, 0x99, 0x0f, 0x5d, - 0xcc, 0x42, 0x1e, 0xf6, 0xeb, 0x7f, 0xfe, 0xf6, 0x6d, 0x45, 0x0b, 0xb7, 0x51, 0x4d, 0x90, 0x8d, - 0x66, 0x10, 0xcc, 0x91, 0x89, 0x38, 0x82, 0x10, 0x24, 0x1d, 0x64, 0x87, 0x4d, 0xa6, 0xfc, 0x9e, - 0xa0, 0xc7, 0xdc, 0x00, 0x29, 0x3b, 0x90, 0x10, 0x4c, 0x1d, 0xd1, 0x5a, 0xfd, 0xe9, 0x2c, 0x28, - 0x86, 0xdb, 0x34, 0xfc, 0xb7, 0x19, 0xf2, 0x63, 0xbf, 0x05, 0x17, 0x9d, 0x93, 0xa8, 0xdf, 0xec, - 0x8c, 0xf7, 0x1e, 0xe5, 0xe5, 0xbc, 0xf7, 0x4c, 0x3f, 0xf3, 0xbd, 0x27, 0xf1, 0x8c, 0xf7, 0x9e, - 0xe4, 0xcb, 0x7b, 0xef, 0x99, 0x79, 0xe9, 0xef, 0x3d, 0xb3, 0xaf, 0xe8, 0xbd, 0x67, 0xee, 0xff, - 0xf2, 0xde, 0x93, 0x7a, 0xa9, 0xef, 0x3d, 0xe9, 0x17, 0x7b, 0xef, 0x01, 0x2f, 0xf4, 0xde, 0x93, - 0x99, 0xe8, 0xbd, 0x47, 0xfd, 0x72, 0x1a, 0xac, 0xc9, 0x49, 0xba, 0xd5, 0x43, 0xae, 0xb8, 0xdc, - 0x51, 0x08, 0x44, 0xe3, 0xb9, 0x32, 0xc1, 0x78, 0x3e, 0xfd, 0x7c, 0xe3, 0x79, 0x62, 0x82, 0xf1, - 0x3c, 0x79, 0xd1, 0x78, 0x3e, 0x73, 0xd1, 0x78, 0x3e, 0x3b, 0xd9, 0x78, 0x3e, 0x77, 0xce, 0x78, - 0x0e, 0x55, 0x30, 0xef, 0x7a, 0x84, 0x8a, 0x3a, 0x10, 0x7b, 0x0b, 0x18, 0x83, 0xa9, 0x05, 0x90, - 0x89, 0x92, 0x88, 0xc9, 0x60, 0x16, 0x24, 0x88, 0x19, 0x36, 0x9d, 0xe2, 0x53, 0xdd, 0x06, 0x97, - 0x2a, 0xa1, 0xea, 0xd8, 0x8c, 0x4f, 0xd0, 0x70, 0x0d, 0xcc, 0xfa, 0x53, 0x6c, 0x40, 0x1f, 0xac, - 0xd4, 0xdf, 0x2b, 0x60, 0xb5, 0xe1, 0x84, 0xde, 0x18, 0xbb, 0x8a, 0x1f, 0x80, 0x8c, 0x49, 0xfb, - 0x1d, 0x0b, 0xeb, 0xa2, 0xc7, 0x09, 0x52, 0xd1, 0xf5, 0x89, 0xea, 0x96, 0xec, 0x8e, 0x6f, 0x21, - 0x62, 0x8d, 0xc4, 0x69, 0xc0, 0x17, 0xd6, 0x22, 0x5d, 0x07, 0xb6, 0x41, 0xca, 0xa4, 0xc7, 0x8e, - 0xcc, 0x2c, 0xd3, 0x2f, 0x28, 0x37, 0x92, 0xa4, 0xfe, 0x5d, 0x01, 0x2b, 0x67, 0x50, 0xc0, 0x1f, - 0x81, 0x45, 0x7f, 0x96, 0x8a, 0x42, 0x4e, 0xd6, 0xc3, 0xdd, 0xf7, 0x44, 0xf4, 0xfe, 0xed, 0x71, - 0xe1, 0xb2, 0x5f, 0x2a, 0x98, 0x79, 0x54, 0x22, 0xb4, 0x6c, 0x23, 0xde, 0x2b, 0xdd, 0xc1, 0x5d, - 0x64, 0x0c, 0x6b, 0xd8, 0xf8, 0xcb, 0x17, 0xd7, 0x40, 0x50, 0x80, 0x6a, 0xd8, 0xf0, 0x4b, 0xc7, - 0x82, 0x94, 0x16, 0x45, 0xe6, 0x1e, 0x58, 0xf8, 0x18, 0x11, 0x4b, 0x0f, 0x7f, 0xe4, 0x08, 0x2c, - 0x9a, 0x28, 0x6d, 0xcc, 0x0b, 0xce, 0x10, 0x2e, 0x3c, 0x91, 0x53, 0xbb, 0xc3, 0x38, 0x75, 0xb0, - 0xf4, 0xd6, 0x94, 0x36, 0x02, 0xbc, 0xfd, 0x07, 0x05, 0x2c, 0x44, 0x5d, 0x5d, 0x0f, 0x31, 0x0c, - 0xf3, 0x60, 0xa3, 0x7a, 0x6f, 0xbf, 0x75, 0xff, 0x6e, 0x5d, 0xd3, 0x9b, 0x7b, 0x95, 0x56, 0x5d, - 0xbf, 0xbf, 0xdf, 0x6a, 0xd6, 0xab, 0x8d, 0x0f, 0x1b, 0xf5, 0x5a, 0x76, 0x0a, 0xbe, 0x0e, 0xd6, - 0x4f, 0xe0, 0xb5, 0xfa, 0xcd, 0x46, 0xab, 0x5d, 0xd7, 0xea, 0xb5, 0xac, 0x72, 0x06, 0x7b, 0x63, - 0xbf, 0xd1, 0x6e, 0x54, 0xee, 0x34, 0x1e, 0xd4, 0x6b, 0xd9, 0x69, 0x78, 0x19, 0x5c, 0x3a, 0x81, - 0xbf, 0x53, 0xb9, 0xbf, 0x5f, 0xdd, 0xab, 0xd7, 0xb2, 0x09, 0xb8, 0x01, 0xd6, 0x4e, 0x20, 0x5b, - 0xed, 0x7b, 0xcd, 0x66, 0xbd, 0x96, 0x4d, 0x9e, 0x81, 0xab, 0xd5, 0xef, 0xd4, 0xdb, 0xf5, 0x5a, - 0x76, 0x66, 0x23, 0xf9, 0xc9, 0xaf, 0xf2, 0x53, 0xbb, 0x1f, 0x7d, 0xf5, 0x24, 0xaf, 0x7c, 0xfd, - 0x24, 0xaf, 0xfc, 0xe3, 0x49, 0x5e, 0xf9, 0xf4, 0x69, 0x7e, 0xea, 0xeb, 0xa7, 0xf9, 0xa9, 0xbf, - 0x3e, 0xcd, 0x4f, 0x3d, 0x78, 0xff, 0x74, 0x25, 0x1f, 0x79, 0xc6, 0xb5, 0xe8, 0xa7, 0x9b, 0xc1, - 0x7b, 0xe5, 0x47, 0xe3, 0xbf, 0x9b, 0xc9, 0x22, 0xdf, 0x99, 0x95, 0xa7, 0xfd, 0xee, 0xff, 0x02, - 0x00, 0x00, 0xff, 0xff, 0xc8, 0x29, 0xb8, 0xcb, 0x68, 0x1b, 0x00, 0x00, + 0xf6, 0xd0, 0x73, 0x2e, 0x05, 0xd2, 0x5b, 0xd0, 0x4b, 0x03, 0xf4, 0x52, 0xf4, 0xd2, 0x1e, 0x82, + 0xfc, 0x80, 0x5e, 0x9a, 0x16, 0x28, 0x90, 0xf6, 0x54, 0x14, 0x85, 0x53, 0xd8, 0x87, 0x1e, 0x0a, + 0xb4, 0xe7, 0xde, 0x8a, 0x99, 0x7d, 0x70, 0xa9, 0x97, 0x69, 0xd8, 0xee, 0x45, 0xda, 0xf9, 0x5e, + 0xf3, 0x7d, 0x33, 0xdf, 0x73, 0x08, 0x76, 0x88, 0xc3, 0xb1, 0x67, 0xf4, 0x10, 0x71, 0x74, 0x86, + 0x8d, 0xbe, 0x47, 0xf8, 0xb0, 0x6c, 0x18, 0x83, 0xb2, 0xeb, 0xd1, 0x01, 0x31, 0xb1, 0x57, 0x1e, + 0x6c, 0x47, 0xdf, 0x25, 0xd7, 0xa3, 0x9c, 0xc2, 0x37, 0xce, 0xe0, 0x29, 0x19, 0xc6, 0xa0, 0x14, + 0xd1, 0x0d, 0xb6, 0x37, 0x96, 0x91, 0x4d, 0x1c, 0x5a, 0x96, 0x7f, 0x7d, 0xbe, 0x8d, 0xbc, 0x41, + 0x99, 0x4d, 0x59, 0xb9, 0x83, 0x18, 0x2e, 0x0f, 0xb6, 0x3b, 0x98, 0xa3, 0xed, 0xb2, 0x41, 0x89, + 0x13, 0xe0, 0xdf, 0x0a, 0xf0, 0x58, 0x08, 0x71, 0x8c, 0x11, 0x4d, 0x08, 0x08, 0xe8, 0xd6, 0x7d, + 0x3a, 0x5d, 0xae, 0xca, 0xfe, 0x22, 0x40, 0xad, 0x76, 0x69, 0x97, 0xfa, 0x70, 0xf1, 0x15, 0x6e, + 0xdc, 0xa5, 0xb4, 0x6b, 0xe1, 0xb2, 0x5c, 0x75, 0xfa, 0x87, 0x65, 0xb3, 0xef, 0x21, 0x4e, 0x68, + 0xb8, 0x71, 0xe1, 0x24, 0x9e, 0x13, 0x1b, 0x33, 0x8e, 0x6c, 0x37, 0x24, 0x20, 0x1d, 0xa3, 0x6c, + 0x50, 0x0f, 0x97, 0x0d, 0x8b, 0x60, 0x87, 0x8b, 0x43, 0xf1, 0xbf, 0x02, 0x82, 0xb2, 0x20, 0xb0, + 0x48, 0xb7, 0xc7, 0x7d, 0x30, 0x2b, 0x73, 0xec, 0x98, 0xd8, 0xb3, 0x89, 0x4f, 0x3c, 0x5a, 0x05, + 0x0c, 0x6f, 0x9e, 0x77, 0xee, 0x83, 0xed, 0xf2, 0x31, 0xf1, 0x42, 0x53, 0x37, 0x63, 0x62, 0x0c, + 0x6f, 0xe8, 0x72, 0x5a, 0x3e, 0xc2, 0xc3, 0xc0, 0x5a, 0xf5, 0xbf, 0x29, 0x90, 0xab, 0x52, 0x87, + 0xf5, 0x6d, 0xec, 0x55, 0x4c, 0x93, 0x08, 0x93, 0x9a, 0x1e, 0x75, 0x29, 0x43, 0x16, 0x5c, 0x05, + 0x33, 0x9c, 0x70, 0x0b, 0xe7, 0x94, 0xa2, 0xb2, 0x95, 0xd6, 0xfc, 0x05, 0x2c, 0x82, 0x8c, 0x89, + 0x99, 0xe1, 0x11, 0x57, 0x10, 0xe7, 0xa6, 0x25, 0x2e, 0x0e, 0x82, 0xeb, 0x20, 0xe5, 0xab, 0x45, + 0xcc, 0x5c, 0x42, 0xa2, 0xe7, 0xe4, 0xba, 0x61, 0xc2, 0x9b, 0x60, 0x91, 0x38, 0x84, 0x13, 0x64, + 0xe9, 0x3d, 0x2c, 0x8c, 0xcd, 0x25, 0x8b, 0xca, 0x56, 0x66, 0x67, 0xa3, 0x44, 0x3a, 0x46, 0x49, + 0x9c, 0x4f, 0x29, 0x38, 0x95, 0xc1, 0x76, 0x69, 0x4f, 0x52, 0xec, 0x26, 0xbf, 0x7a, 0x5c, 0x98, + 0xd2, 0x16, 0x02, 0x3e, 0x1f, 0x08, 0xaf, 0x80, 0xf9, 0x2e, 0x76, 0x30, 0x23, 0x4c, 0xef, 0x21, + 0xd6, 0xcb, 0xcd, 0x14, 0x95, 0xad, 0x79, 0x2d, 0x13, 0xc0, 0xf6, 0x10, 0xeb, 0xc1, 0x02, 0xc8, + 0x74, 0x88, 0x83, 0xbc, 0xa1, 0x4f, 0x31, 0x2b, 0x29, 0x80, 0x0f, 0x92, 0x04, 0x55, 0x00, 0x98, + 0x8b, 0x8e, 0x1d, 0x5d, 0x5c, 0x56, 0x6e, 0x2e, 0x50, 0xc4, 0xbf, 0xc9, 0x52, 0x78, 0x93, 0xa5, + 0x76, 0x78, 0x93, 0xbb, 0x29, 0xa1, 0xc8, 0xa7, 0xdf, 0x14, 0x14, 0x2d, 0x2d, 0xf9, 0x04, 0x06, + 0xee, 0x83, 0x6c, 0xdf, 0xe9, 0x50, 0xc7, 0x24, 0x4e, 0x57, 0x77, 0xb1, 0x47, 0xa8, 0x99, 0x4b, + 0x49, 0x51, 0xeb, 0xa7, 0x44, 0xd5, 0x02, 0xa7, 0xf1, 0x25, 0x7d, 0x26, 0x24, 0x2d, 0x45, 0xcc, + 0x4d, 0xc9, 0x0b, 0xbf, 0x0f, 0xa0, 0x61, 0x0c, 0xa4, 0x4a, 0xb4, 0xcf, 0x43, 0x89, 0xe9, 0xc9, + 0x25, 0x66, 0x0d, 0x63, 0xd0, 0xf6, 0xb9, 0x03, 0x91, 0x3f, 0x04, 0x97, 0xb8, 0x87, 0x1c, 0x76, + 0x88, 0xbd, 0x93, 0x72, 0xc1, 0xe4, 0x72, 0x5f, 0x0b, 0x65, 0x8c, 0x0b, 0xdf, 0x03, 0x45, 0x23, + 0x70, 0x20, 0xdd, 0xc3, 0x26, 0x61, 0xdc, 0x23, 0x9d, 0xbe, 0xe0, 0xd5, 0x0f, 0x3d, 0x64, 0x48, + 0x1f, 0xc9, 0x48, 0x27, 0xc8, 0x87, 0x74, 0xda, 0x18, 0xd9, 0x87, 0x01, 0x15, 0xbc, 0x07, 0xbe, + 0xd5, 0xb1, 0xa8, 0x71, 0xc4, 0x84, 0x72, 0xfa, 0x98, 0x24, 0xb9, 0xb5, 0x4d, 0x18, 0x13, 0xd2, + 0xe6, 0x8b, 0xca, 0x56, 0x42, 0xbb, 0xe2, 0xd3, 0x36, 0xb1, 0x57, 0x8b, 0x51, 0xb6, 0x63, 0x84, + 0xf0, 0x1a, 0x80, 0x3d, 0xc2, 0x38, 0xf5, 0x88, 0x81, 0x2c, 0x1d, 0x3b, 0xdc, 0x23, 0x98, 0xe5, + 0x16, 0x24, 0xfb, 0xf2, 0x08, 0x53, 0xf7, 0x11, 0xf0, 0x16, 0xb8, 0x72, 0xee, 0xa6, 0xba, 0xd1, + 0x43, 0x8e, 0x83, 0xad, 0xdc, 0xa2, 0x34, 0xa5, 0x60, 0x9e, 0xb3, 0x67, 0xd5, 0x27, 0x83, 0x2b, + 0x60, 0x86, 0x53, 0x57, 0xdf, 0xcf, 0x2d, 0x15, 0x95, 0xad, 0x05, 0x2d, 0xc9, 0xa9, 0xbb, 0x0f, + 0xdf, 0x01, 0xab, 0x03, 0x64, 0x11, 0x13, 0x71, 0xea, 0x31, 0xdd, 0xa5, 0xc7, 0xd8, 0xd3, 0x0d, + 0xe4, 0xe6, 0xb2, 0x92, 0x06, 0x8e, 0x70, 0x4d, 0x81, 0xaa, 0x22, 0x17, 0xbe, 0x0d, 0x96, 0x23, + 0xa8, 0xce, 0x30, 0x97, 0xe4, 0xcb, 0x92, 0x7c, 0x29, 0x42, 0xb4, 0x30, 0x17, 0xb4, 0x9b, 0x20, + 0x8d, 0x2c, 0x8b, 0x1e, 0x5b, 0x84, 0xf1, 0x1c, 0x2c, 0x26, 0xb6, 0xd2, 0xda, 0x08, 0x00, 0x37, + 0x40, 0xca, 0xc4, 0xce, 0x50, 0x22, 0x57, 0x24, 0x32, 0x5a, 0xc3, 0xcb, 0x20, 0x6d, 0x8b, 0x24, + 0xc2, 0xd1, 0x11, 0xce, 0xad, 0x16, 0x95, 0xad, 0xa4, 0x96, 0xb2, 0x89, 0xd3, 0x12, 0x6b, 0x58, + 0x02, 0x2b, 0x52, 0x8a, 0x4e, 0x1c, 0x71, 0x4f, 0x03, 0xac, 0x0f, 0x90, 0xc5, 0x72, 0xaf, 0x15, + 0x95, 0xad, 0x94, 0xb6, 0x2c, 0x51, 0x8d, 0x00, 0x73, 0x80, 0x2c, 0x76, 0x63, 0xeb, 0x93, 0xcf, + 0x0b, 0x53, 0x9f, 0x7d, 0x5e, 0x98, 0xfa, 0xe3, 0x17, 0xd7, 0x36, 0x82, 0xcc, 0xda, 0xa5, 0x83, + 0x52, 0x90, 0x89, 0x4b, 0x55, 0xea, 0x70, 0xec, 0xf0, 0x9c, 0xa2, 0xfe, 0x59, 0x01, 0x97, 0xaa, + 0x91, 0x4b, 0xd8, 0x74, 0x80, 0xac, 0x57, 0x99, 0x7a, 0x2a, 0x20, 0xcd, 0xc4, 0x9d, 0xc8, 0x60, + 0x4f, 0x3e, 0x47, 0xb0, 0xa7, 0x04, 0x9b, 0x40, 0xdc, 0x28, 0x3e, 0xd3, 0xa6, 0xff, 0x4c, 0x83, + 0xcd, 0xd0, 0xa6, 0xbb, 0xd4, 0x24, 0x87, 0xc4, 0x40, 0xaf, 0x3a, 0xa7, 0x46, 0xbe, 0x96, 0x9c, + 0xc0, 0xd7, 0x66, 0x9e, 0xcf, 0xd7, 0x66, 0x27, 0xf0, 0xb5, 0xb9, 0x8b, 0x7c, 0x2d, 0x75, 0x91, + 0xaf, 0xa5, 0x27, 0xf3, 0x35, 0x70, 0x9e, 0xaf, 0x4d, 0xe7, 0x14, 0xf5, 0x17, 0x0a, 0x58, 0xad, + 0x3f, 0xec, 0x93, 0x01, 0x7d, 0x49, 0x27, 0x7d, 0x1b, 0x2c, 0xe0, 0x98, 0x3c, 0x96, 0x4b, 0x14, + 0x13, 0x5b, 0x99, 0x9d, 0x37, 0x4b, 0xc1, 0xc5, 0x47, 0xad, 0x44, 0x78, 0xfb, 0xf1, 0xdd, 0xb5, + 0x71, 0x5e, 0xa9, 0xe1, 0xef, 0x14, 0xb0, 0x21, 0xf2, 0x42, 0x17, 0x6b, 0xf8, 0x18, 0x79, 0x66, + 0x0d, 0x3b, 0xd4, 0x66, 0x2f, 0xac, 0xa7, 0x0a, 0x16, 0x4c, 0x29, 0x49, 0xe7, 0x54, 0x47, 0xa6, + 0x29, 0xf5, 0x94, 0x34, 0x02, 0xd8, 0xa6, 0x15, 0xd3, 0x84, 0x5b, 0x20, 0x3b, 0xa2, 0xf1, 0x44, + 0x8c, 0x09, 0xd7, 0x17, 0x64, 0x8b, 0x21, 0x99, 0x8c, 0x3c, 0x7c, 0x23, 0x7f, 0xb1, 0x6b, 0xab, + 0xff, 0x52, 0x40, 0xf6, 0xa6, 0x45, 0x3b, 0xc8, 0x6a, 0x59, 0x88, 0xf5, 0x44, 0xce, 0x1c, 0x8a, + 0x90, 0xf2, 0x70, 0x50, 0xac, 0xa4, 0xfa, 0x13, 0x87, 0x94, 0x60, 0x93, 0xe5, 0xf3, 0x03, 0xb0, + 0x1c, 0x95, 0x8f, 0xc8, 0xc1, 0xa5, 0xb5, 0xbb, 0x2b, 0x4f, 0x1e, 0x17, 0x96, 0xc2, 0x60, 0xaa, + 0x4a, 0x67, 0xaf, 0x69, 0x4b, 0xc6, 0x18, 0xc0, 0x84, 0x79, 0x90, 0x21, 0x1d, 0x43, 0x67, 0xf8, + 0xa1, 0xee, 0xf4, 0x6d, 0x19, 0x1b, 0x49, 0x2d, 0x4d, 0x3a, 0x46, 0x0b, 0x3f, 0xdc, 0xef, 0xdb, + 0xf0, 0x5d, 0xb0, 0x16, 0x36, 0x95, 0xc2, 0x9b, 0x74, 0xc1, 0x2f, 0x8e, 0xcb, 0x93, 0xe1, 0x32, + 0xaf, 0xad, 0x84, 0xd8, 0x03, 0x64, 0x89, 0xcd, 0x2a, 0xa6, 0xe9, 0xa9, 0xff, 0x9e, 0x01, 0xb3, + 0x4d, 0xe4, 0x21, 0x9b, 0xc1, 0x36, 0x58, 0xe2, 0xd8, 0x76, 0x2d, 0xc4, 0xb1, 0xee, 0xb7, 0x26, + 0x81, 0xa5, 0x57, 0x65, 0xcb, 0x12, 0xef, 0xd8, 0x4a, 0xb1, 0x1e, 0x6d, 0xb0, 0x5d, 0xaa, 0x4a, + 0x68, 0x8b, 0x23, 0x8e, 0xb5, 0xc5, 0x50, 0x86, 0x0f, 0x84, 0xd7, 0x41, 0x8e, 0x7b, 0x7d, 0xc6, + 0x47, 0x4d, 0xc3, 0xa8, 0x5a, 0xfa, 0x77, 0xbd, 0x16, 0xe2, 0xfd, 0x3a, 0x1b, 0x55, 0xc9, 0xb3, + 0xfb, 0x83, 0xc4, 0x8b, 0xf4, 0x07, 0x26, 0xd8, 0x64, 0xe2, 0x52, 0x75, 0x1b, 0x73, 0x59, 0xc5, + 0x5d, 0x0b, 0x3b, 0x84, 0xf5, 0x42, 0xe1, 0xb3, 0x93, 0x0b, 0x5f, 0x97, 0x82, 0xee, 0x0a, 0x39, + 0x5a, 0x28, 0x26, 0xd8, 0xa5, 0x0a, 0xf2, 0x67, 0xef, 0x12, 0x19, 0x3e, 0x27, 0x0d, 0xbf, 0x7c, + 0x86, 0x88, 0xc8, 0x7a, 0x06, 0xde, 0x8a, 0x75, 0x1b, 0x22, 0x9a, 0x74, 0xe9, 0xc8, 0xba, 0x87, + 0xbb, 0xa2, 0x24, 0x23, 0xbf, 0xf1, 0xc0, 0x38, 0xea, 0x98, 0x02, 0x9f, 0x16, 0x13, 0x43, 0xcc, + 0xa9, 0x89, 0x13, 0xb4, 0x95, 0xea, 0xa8, 0x29, 0x89, 0x62, 0x53, 0x8b, 0xc9, 0xfa, 0x10, 0x63, + 0x11, 0x45, 0xb1, 0xc6, 0x04, 0xbb, 0xd4, 0xe8, 0xc9, 0x9c, 0x94, 0xd0, 0x16, 0xa3, 0x26, 0xa4, + 0x2e, 0xa0, 0xf0, 0x01, 0xb8, 0xea, 0xf4, 0xed, 0x0e, 0xf6, 0x74, 0x7a, 0xe8, 0x13, 0xca, 0xc8, + 0x63, 0x1c, 0x79, 0x5c, 0xf7, 0xb0, 0x81, 0xc9, 0x40, 0xdc, 0xb8, 0xaf, 0x39, 0x93, 0x7d, 0x51, + 0x42, 0x7b, 0xd3, 0x67, 0xb9, 0x77, 0x28, 0x65, 0xb0, 0x36, 0x6d, 0x09, 0x72, 0x2d, 0xa4, 0xf6, + 0x15, 0x63, 0xb0, 0x01, 0xae, 0xd8, 0xe8, 0x91, 0x1e, 0x39, 0xb3, 0x50, 0x1c, 0x3b, 0xac, 0xcf, + 0xf4, 0x51, 0x32, 0x0f, 0x7a, 0xa3, 0xbc, 0x8d, 0x1e, 0x35, 0x03, 0xba, 0x6a, 0x48, 0x76, 0x10, + 0x51, 0xdd, 0x4a, 0xa6, 0x92, 0xd9, 0x99, 0x5b, 0xc9, 0xd4, 0x4c, 0x76, 0xf6, 0x56, 0x32, 0x95, + 0xca, 0xa6, 0xd5, 0x6f, 0x83, 0xb4, 0x8c, 0xeb, 0x8a, 0x71, 0xc4, 0x64, 0x76, 0x37, 0x4d, 0x0f, + 0x33, 0x86, 0x59, 0x4e, 0x09, 0xb2, 0x7b, 0x08, 0x50, 0x39, 0x58, 0x3f, 0x6f, 0x62, 0x60, 0xf0, + 0x23, 0x30, 0xe7, 0x62, 0xd9, 0xce, 0x4a, 0xc6, 0xcc, 0xce, 0xfb, 0xa5, 0x09, 0x46, 0xbd, 0xd2, + 0x79, 0x02, 0xb5, 0x50, 0x9a, 0xea, 0x8d, 0xe6, 0x94, 0x13, 0xbd, 0x02, 0x83, 0x07, 0x27, 0x37, + 0xfd, 0xde, 0x73, 0x6d, 0x7a, 0x42, 0xde, 0x68, 0xcf, 0xab, 0x20, 0x53, 0xf1, 0xcd, 0xbe, 0x23, + 0x4a, 0xd7, 0xa9, 0x63, 0x99, 0x8f, 0x1f, 0xcb, 0x3e, 0x58, 0x0c, 0x9a, 0xbf, 0x36, 0x95, 0xb9, + 0x09, 0xbe, 0x0e, 0x40, 0xd0, 0x35, 0x8a, 0x9c, 0xe6, 0x67, 0xf7, 0x74, 0x00, 0x69, 0x98, 0x63, + 0x15, 0x7d, 0x7a, 0xac, 0xa2, 0xcb, 0xaa, 0x41, 0xc1, 0xfa, 0x41, 0xbc, 0xea, 0xca, 0x02, 0xd2, + 0x44, 0xc6, 0x11, 0xe6, 0x0c, 0x6a, 0x20, 0x29, 0xab, 0xab, 0x6f, 0xee, 0xf5, 0x73, 0xcd, 0x1d, + 0x6c, 0x97, 0xce, 0x13, 0x52, 0x43, 0x1c, 0x05, 0x31, 0x20, 0x65, 0xa9, 0x3f, 0x53, 0x40, 0xee, + 0x36, 0x1e, 0x56, 0x18, 0x23, 0x5d, 0xc7, 0xc6, 0x0e, 0x17, 0xd1, 0x87, 0x0c, 0x2c, 0x3e, 0xe1, + 0x1b, 0x60, 0x21, 0x72, 0x3c, 0x99, 0x3c, 0x15, 0x99, 0x3c, 0xe7, 0x43, 0xa0, 0x38, 0x27, 0x78, + 0x03, 0x00, 0xd7, 0xc3, 0x03, 0xdd, 0xd0, 0x8f, 0xf0, 0x50, 0xda, 0x94, 0xd9, 0xd9, 0x8c, 0x27, + 0x45, 0x7f, 0xfe, 0x2c, 0x35, 0xfb, 0x1d, 0x8b, 0x18, 0xb7, 0xf1, 0x50, 0x4b, 0x09, 0xfa, 0xea, + 0x6d, 0x3c, 0x14, 0x55, 0x50, 0x36, 0x29, 0x32, 0x93, 0x25, 0x34, 0x7f, 0xa1, 0xfe, 0x5c, 0x01, + 0x97, 0x22, 0x03, 0xc2, 0xfb, 0x6a, 0xf6, 0x3b, 0x82, 0x23, 0x7e, 0x7e, 0xca, 0x78, 0x47, 0x74, + 0x4a, 0xdb, 0xe9, 0x33, 0xb4, 0xfd, 0x00, 0xcc, 0x47, 0xa9, 0x44, 0xe8, 0x9b, 0x98, 0x40, 0xdf, + 0x4c, 0xc8, 0x71, 0x1b, 0x0f, 0xd5, 0x9f, 0xc4, 0x74, 0xdb, 0x1d, 0xc6, 0x5c, 0xd8, 0x7b, 0x86, + 0x6e, 0xd1, 0xb6, 0x71, 0xdd, 0x8c, 0x38, 0xff, 0x29, 0x03, 0x12, 0xa7, 0x0d, 0x50, 0xff, 0xa4, + 0x80, 0xb5, 0xf8, 0xae, 0xac, 0x4d, 0x9b, 0x5e, 0xdf, 0xc1, 0x07, 0x3b, 0x17, 0xed, 0xff, 0x01, + 0x48, 0xb9, 0x82, 0x4a, 0xe7, 0x2c, 0xb8, 0xa2, 0xc9, 0x4a, 0xf6, 0x9c, 0xe4, 0x6a, 0x8b, 0x10, + 0x5f, 0x1c, 0x33, 0x80, 0x05, 0x27, 0xf7, 0xce, 0x44, 0x41, 0x17, 0x0b, 0x28, 0x6d, 0x21, 0x6e, + 0x33, 0x53, 0xbf, 0x54, 0x00, 0x3c, 0x9d, 0xad, 0xe0, 0x77, 0x00, 0x1c, 0xcb, 0x79, 0x71, 0xff, + 0xcb, 0xba, 0xb1, 0x2c, 0x27, 0x4f, 0x2e, 0xf2, 0xa3, 0xe9, 0x98, 0x1f, 0xc1, 0xef, 0x02, 0xe0, + 0xca, 0x4b, 0x9c, 0xf8, 0xa6, 0xd3, 0x6e, 0xf8, 0x09, 0x0b, 0x20, 0xf3, 0x31, 0x25, 0x4e, 0xfc, + 0xc1, 0x22, 0xa1, 0x01, 0x01, 0xf2, 0xdf, 0x22, 0xd4, 0x9f, 0x2a, 0xa3, 0x94, 0x18, 0x64, 0xeb, + 0x8a, 0x65, 0x05, 0x3d, 0x20, 0x74, 0xc1, 0x5c, 0x98, 0xef, 0xfd, 0x70, 0xdd, 0x3c, 0xb3, 0x26, + 0xd5, 0xb0, 0x21, 0xcb, 0xd2, 0x75, 0x71, 0xe2, 0xbf, 0xfe, 0xa6, 0x70, 0xb5, 0x4b, 0x78, 0xaf, + 0xdf, 0x29, 0x19, 0xd4, 0x0e, 0x1e, 0xa8, 0x82, 0x7f, 0xd7, 0x98, 0x79, 0x54, 0xe6, 0x43, 0x17, + 0xb3, 0x90, 0x87, 0xfd, 0xea, 0x9f, 0xbf, 0x7d, 0x5b, 0xd1, 0xc2, 0x6d, 0x54, 0x13, 0x64, 0xa3, + 0x19, 0x04, 0x73, 0x64, 0x22, 0x8e, 0x20, 0x04, 0x49, 0x07, 0xd9, 0x61, 0x93, 0x29, 0xbf, 0x27, + 0xe8, 0x31, 0x37, 0x40, 0xca, 0x0e, 0x24, 0x04, 0x53, 0x47, 0xb4, 0x56, 0x7f, 0x33, 0x0b, 0x8a, + 0xe1, 0x36, 0x0d, 0xff, 0x6d, 0x86, 0xfc, 0xd8, 0x6f, 0xc1, 0x45, 0xe7, 0x24, 0xea, 0x37, 0x3b, + 0xe3, 0xbd, 0x47, 0x79, 0x39, 0xef, 0x3d, 0xd3, 0xcf, 0x7c, 0xef, 0x49, 0x3c, 0xe3, 0xbd, 0x27, + 0xf9, 0xf2, 0xde, 0x7b, 0x66, 0x5e, 0xfa, 0x7b, 0xcf, 0xec, 0x2b, 0x7a, 0xef, 0x99, 0xfb, 0xbf, + 0xbc, 0xf7, 0xa4, 0x5e, 0xea, 0x7b, 0x4f, 0xfa, 0xc5, 0xde, 0x7b, 0xc0, 0x0b, 0xbd, 0xf7, 0x64, + 0x26, 0x7b, 0xef, 0xf1, 0xb3, 0xba, 0x83, 0xa5, 0x65, 0x22, 0xeb, 0xce, 0x4b, 0xbe, 0xf9, 0x11, + 0xb0, 0x61, 0xaa, 0x5f, 0x4e, 0x83, 0x35, 0x39, 0x6e, 0xb7, 0x7a, 0xc8, 0x15, 0x1e, 0x30, 0x8a, + 0x93, 0x68, 0x86, 0x57, 0x26, 0x98, 0xe1, 0xa7, 0x9f, 0x6f, 0x86, 0x4f, 0x4c, 0x30, 0xc3, 0x27, + 0x2f, 0x9a, 0xe1, 0x67, 0x2e, 0x9a, 0xe1, 0x67, 0x27, 0x9b, 0xe1, 0xe7, 0xce, 0x99, 0xe1, 0xa1, + 0x0a, 0xe6, 0x5d, 0x8f, 0x50, 0x51, 0x2c, 0x62, 0x0f, 0x06, 0x63, 0x30, 0xb5, 0x00, 0x32, 0x51, + 0xa6, 0x31, 0x19, 0xcc, 0x82, 0x04, 0x31, 0xc3, 0xce, 0x54, 0x7c, 0xaa, 0xdb, 0xe0, 0x52, 0x25, + 0x54, 0x1d, 0x9b, 0xf1, 0x31, 0x1b, 0xae, 0x81, 0x59, 0x7f, 0xd4, 0x0d, 0xe8, 0x83, 0x95, 0xfa, + 0x7b, 0x05, 0xac, 0x36, 0x9c, 0xd0, 0x65, 0x63, 0x57, 0xf1, 0x03, 0x90, 0x31, 0x69, 0xbf, 0x63, + 0x61, 0x5d, 0x34, 0x42, 0x41, 0xbe, 0xba, 0x3e, 0x51, 0x71, 0x93, 0x2d, 0xf4, 0x2d, 0x44, 0xac, + 0x91, 0x38, 0x0d, 0xf8, 0xc2, 0x5a, 0xa4, 0xeb, 0xc0, 0x36, 0x48, 0x99, 0xf4, 0xd8, 0x91, 0xe9, + 0x67, 0xfa, 0x05, 0xe5, 0x46, 0x92, 0xd4, 0xbf, 0x2b, 0x60, 0xe5, 0x0c, 0x0a, 0xf8, 0x23, 0xb0, + 0xe8, 0x0f, 0x5c, 0x51, 0x5c, 0xca, 0xa2, 0xb9, 0xfb, 0x9e, 0x08, 0xf1, 0xbf, 0x3d, 0x2e, 0x5c, + 0xf6, 0xeb, 0x09, 0x33, 0x8f, 0x4a, 0x84, 0x96, 0x6d, 0xc4, 0x7b, 0xa5, 0x3b, 0xb8, 0x8b, 0x8c, + 0x61, 0x0d, 0x1b, 0x7f, 0xf9, 0xe2, 0x1a, 0x08, 0xaa, 0x54, 0x0d, 0x1b, 0x7e, 0x7d, 0x59, 0x90, + 0xd2, 0xa2, 0xf0, 0xdd, 0x03, 0x0b, 0x1f, 0x23, 0x62, 0xe9, 0xe1, 0x2f, 0x21, 0x81, 0x45, 0x13, + 0xe5, 0x96, 0x79, 0xc1, 0x19, 0xc2, 0x85, 0x27, 0x72, 0x6a, 0x77, 0x18, 0xa7, 0x0e, 0x96, 0xde, + 0x9a, 0xd2, 0x46, 0x80, 0xb7, 0xff, 0xa0, 0x80, 0x85, 0xa8, 0xf5, 0xeb, 0x21, 0x86, 0x61, 0x1e, + 0x6c, 0x54, 0xef, 0xed, 0xb7, 0xee, 0xdf, 0xad, 0x6b, 0x7a, 0x73, 0xaf, 0xd2, 0xaa, 0xeb, 0xf7, + 0xf7, 0x5b, 0xcd, 0x7a, 0xb5, 0xf1, 0x61, 0xa3, 0x5e, 0xcb, 0x4e, 0xc1, 0xd7, 0xc1, 0xfa, 0x09, + 0xbc, 0x56, 0xbf, 0xd9, 0x68, 0xb5, 0xeb, 0x5a, 0xbd, 0x96, 0x55, 0xce, 0x60, 0x6f, 0xec, 0x37, + 0xda, 0x8d, 0xca, 0x9d, 0xc6, 0x83, 0x7a, 0x2d, 0x3b, 0x0d, 0x2f, 0x83, 0x4b, 0x27, 0xf0, 0x77, + 0x2a, 0xf7, 0xf7, 0xab, 0x7b, 0xf5, 0x5a, 0x36, 0x01, 0x37, 0xc0, 0xda, 0x09, 0x64, 0xab, 0x7d, + 0xaf, 0xd9, 0xac, 0xd7, 0xb2, 0xc9, 0x33, 0x70, 0xb5, 0xfa, 0x9d, 0x7a, 0xbb, 0x5e, 0xcb, 0xce, + 0x6c, 0x24, 0x3f, 0xf9, 0x65, 0x7e, 0x6a, 0xf7, 0xa3, 0xaf, 0x9e, 0xe4, 0x95, 0xaf, 0x9f, 0xe4, + 0x95, 0x7f, 0x3c, 0xc9, 0x2b, 0x9f, 0x3e, 0xcd, 0x4f, 0x7d, 0xfd, 0x34, 0x3f, 0xf5, 0xd7, 0xa7, + 0xf9, 0xa9, 0x07, 0xef, 0x9f, 0x2e, 0xf7, 0x23, 0xcf, 0xb8, 0x16, 0xfd, 0xbe, 0x33, 0x78, 0xaf, + 0xfc, 0x68, 0xfc, 0xc7, 0x35, 0xd9, 0x09, 0x74, 0x66, 0xe5, 0x69, 0xbf, 0xfb, 0xbf, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xaa, 0x8f, 0x44, 0x18, 0x8d, 0x1b, 0x00, 0x00, } func (m *ConsumerAdditionProposal) Marshal() (dAtA []byte, err error) { @@ -3313,6 +3327,13 @@ func (m *ConsumerInitializationParameters) MarshalToSizedBuffer(dAtA []byte) (in _ = i var l int _ = l + if len(m.ConnectionId) > 0 { + i -= len(m.ConnectionId) + copy(dAtA[i:], m.ConnectionId) + i = encodeVarintProvider(dAtA, i, uint64(len(m.ConnectionId))) + i-- + dAtA[i] = 0x62 + } if len(m.DistributionTransmissionChannel) > 0 { i -= len(m.DistributionTransmissionChannel) copy(dAtA[i:], m.DistributionTransmissionChannel) @@ -4180,6 +4201,10 @@ func (m *ConsumerInitializationParameters) Size() (n int) { if l > 0 { n += 1 + l + sovProvider(uint64(l)) } + l = len(m.ConnectionId) + if l > 0 { + n += 1 + l + sovProvider(uint64(l)) + } return n } @@ -8094,6 +8119,38 @@ func (m *ConsumerInitializationParameters) Unmarshal(dAtA []byte) error { } m.DistributionTransmissionChannel = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 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.ConnectionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipProvider(dAtA[iNdEx:]) diff --git a/x/ccv/types/genesis.go b/x/ccv/types/genesis.go index a7534b1289..632e6c2b7b 100644 --- a/x/ccv/types/genesis.go +++ b/x/ccv/types/genesis.go @@ -1,6 +1,8 @@ package types import ( + "strings" + ibctmtypes "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" errorsmod "cosmossdk.io/errors" @@ -9,8 +11,13 @@ import ( ) // NewInitialConsumerGenesisState returns a ConsumerGenesisState for a completely new consumer chain. -func NewInitialConsumerGenesisState(cs *ibctmtypes.ClientState, consState *ibctmtypes.ConsensusState, - initValSet []abci.ValidatorUpdate, params ConsumerParams, +func NewInitialConsumerGenesisState( + cs *ibctmtypes.ClientState, + consState *ibctmtypes.ConsensusState, + initValSet []abci.ValidatorUpdate, + preCCV bool, + connectionId string, + params ConsumerParams, ) *ConsumerGenesisState { return &ConsumerGenesisState{ NewChain: true, @@ -20,6 +27,8 @@ func NewInitialConsumerGenesisState(cs *ibctmtypes.ClientState, consState *ibctm ConsensusState: consState, InitialValSet: initValSet, }, + PreCCV: preCCV, + ConnectionId: connectionId, } } @@ -41,8 +50,28 @@ func (gs ConsumerGenesisState) Validate() error { if err := gs.Params.Validate(); err != nil { return err } + if !gs.NewChain { + // consumer genesis should be for a new chain only + return errorsmod.Wrapf(ErrInvalidGenesis, "NewChain must be set to true") + } - if gs.NewChain { + if gs.PreCCV { + // consumer chain MUST start in pre-CCV state, i.e., + // the consumer CCV module MUST NOT pass validator updates + // to the underlying consensus engine + if gs.Provider.ClientState != nil || gs.Provider.ConsensusState != nil { + return errorsmod.Wrap(ErrInvalidGenesis, "provider client state and consensus state must be nil for a restarting genesis state") + } + if err := ValidateConnectionIdentifier(gs.ConnectionId); err != nil { + return errorsmod.Wrapf(ErrInvalidGenesis, "ConnectionId: %s", err.Error()) + } + if strings.TrimSpace(gs.ConnectionId) == "" { + return errorsmod.Wrapf(ErrInvalidGenesis, "ConnectionId cannot be empty when preCCV is true") + } + } else { + // consumer chain MUST NOT start in pre-CCV state, i.e., + // the consumer CCV module MUST pass validator updates + // to the underlying consensus engine if gs.Provider.ClientState == nil { return errorsmod.Wrap(ErrInvalidGenesis, "provider client state cannot be nil for new chain") } @@ -55,8 +84,9 @@ func (gs ConsumerGenesisState) Validate() error { if err := gs.Provider.ConsensusState.ValidateBasic(); err != nil { return errorsmod.Wrapf(ErrInvalidGenesis, "provider consensus state invalid for new chain %s", err.Error()) } - } else if gs.Provider.ClientState != nil || gs.Provider.ConsensusState != nil { - return errorsmod.Wrap(ErrInvalidGenesis, "provider client state and consensus state must be nil for a restarting genesis state") + if strings.TrimSpace(gs.ConnectionId) != "" { + return errorsmod.Wrapf(ErrInvalidGenesis, "ConnectionId must be empty when preCCV is false") + } } return nil } diff --git a/x/ccv/types/shared_consumer.pb.go b/x/ccv/types/shared_consumer.pb.go index b9dd99caf0..c6195c79f3 100644 --- a/x/ccv/types/shared_consumer.pb.go +++ b/x/ccv/types/shared_consumer.pb.go @@ -217,8 +217,17 @@ func (m *ConsumerParams) GetConsumerId() string { type ConsumerGenesisState struct { Params ConsumerParams `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` Provider ProviderInfo `protobuf:"bytes,2,opt,name=provider,proto3" json:"provider"` - // true for new chain, false for chain restart. + // True for new chain, false for chain restart. + // This is needed and always set to true; otherwise, new_chain in the consumer + // genesis state will default to false NewChain bool `protobuf:"varint,3,opt,name=new_chain,json=newChain,proto3" json:"new_chain,omitempty"` + // Flag indicating whether the consumer CCV module starts in pre-CCV state + PreCCV bool `protobuf:"varint,4,opt,name=preCCV,proto3" json:"preCCV,omitempty"` + // The ID of the connection end on the consumer chain on top of which the + // CCV channel will be established. If connection_id == "", a new client of + // the provider chain and a new connection on top of this client are created. + // The new client is initialized using client_state and consensus_state. + ConnectionId string `protobuf:"bytes,5,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty"` } func (m *ConsumerGenesisState) Reset() { *m = ConsumerGenesisState{} } @@ -275,12 +284,28 @@ func (m *ConsumerGenesisState) GetNewChain() bool { return false } +func (m *ConsumerGenesisState) GetPreCCV() bool { + if m != nil { + return m.PreCCV + } + return false +} + +func (m *ConsumerGenesisState) GetConnectionId() string { + if m != nil { + return m.ConnectionId + } + return "" +} + // ProviderInfo defines all information a consumer needs from a provider // Shared data type between provider and consumer type ProviderInfo struct { - // ProviderClientState filled in on new chain, nil on restart. + // The client state for the provider client filled in on new chain, nil on restart. + // If connection_id != "", then client_state is ignored. ClientState *_07_tendermint.ClientState `protobuf:"bytes,1,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty"` - // ProviderConsensusState filled in on new chain, nil on restart. + // The consensus state for the provider client filled in on new chain, nil on restart. + // If connection_id != "", then consensus_state is ignored. ConsensusState *_07_tendermint.ConsensusState `protobuf:"bytes,2,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty"` // InitialValset filled in on new chain and on restart. InitialValSet []types.ValidatorUpdate `protobuf:"bytes,3,rep,name=initial_val_set,json=initialValSet,proto3" json:"initial_val_set"` @@ -351,60 +376,61 @@ func init() { } var fileDescriptor_d0a8be0efc64dfbc = []byte{ - // 833 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0xcf, 0x73, 0xdc, 0x34, - 0x14, 0x8e, 0xb3, 0x25, 0xd9, 0x68, 0xf3, 0xa3, 0x88, 0x50, 0x4c, 0x3a, 0xb3, 0xd9, 0x06, 0x0e, - 0x3b, 0x30, 0xb5, 0x49, 0xe8, 0xc0, 0x0c, 0x37, 0x92, 0x50, 0xda, 0x1e, 0x92, 0xad, 0x13, 0xca, - 0x0c, 0x1c, 0x34, 0xb2, 0xf4, 0x76, 0xad, 0xc1, 0x96, 0x3c, 0x92, 0xec, 0x90, 0x3b, 0x33, 0x5c, - 0x39, 0xf2, 0x27, 0x95, 0x5b, 0x8f, 0x9c, 0x80, 0x49, 0xfe, 0x11, 0xc6, 0xb2, 0xbd, 0xf1, 0x32, - 0x04, 0xda, 0x9b, 0x9f, 0xf4, 0x7d, 0x9f, 0xf5, 0xbd, 0xa7, 0xf7, 0x84, 0x3e, 0x11, 0xd2, 0x82, - 0x66, 0x09, 0x15, 0x92, 0x18, 0x60, 0x85, 0x16, 0xf6, 0x32, 0x64, 0xac, 0x0c, 0xcb, 0xfd, 0xd0, - 0x24, 0x54, 0x03, 0x27, 0x4c, 0x49, 0x53, 0x64, 0xa0, 0x83, 0x5c, 0x2b, 0xab, 0xf0, 0xce, 0xbf, - 0x30, 0x02, 0xc6, 0xca, 0xa0, 0xdc, 0xdf, 0xb9, 0x6f, 0x41, 0x72, 0xd0, 0x99, 0x90, 0x36, 0xa4, - 0x31, 0x13, 0xa1, 0xbd, 0xcc, 0xc1, 0xd4, 0xc4, 0x9d, 0x50, 0xc4, 0x2c, 0x4c, 0xc5, 0x2c, 0xb1, - 0x2c, 0x15, 0x20, 0xad, 0x09, 0x3b, 0xe8, 0x72, 0xbf, 0x13, 0x35, 0x84, 0xe1, 0x4c, 0xa9, 0x59, - 0x0a, 0xa1, 0x8b, 0xe2, 0x62, 0x1a, 0xf2, 0x42, 0x53, 0x2b, 0x94, 0x6c, 0xf6, 0xb7, 0x67, 0x6a, - 0xa6, 0xdc, 0x67, 0x58, 0x7d, 0xd5, 0xab, 0x7b, 0x3f, 0xad, 0xa2, 0xcd, 0xa3, 0xe6, 0xc8, 0x13, - 0xaa, 0x69, 0x66, 0xb0, 0x8f, 0x56, 0x41, 0xd2, 0x38, 0x05, 0xee, 0x7b, 0x23, 0x6f, 0xdc, 0x8f, - 0xda, 0x10, 0x9f, 0xa2, 0x0f, 0xe3, 0x54, 0xb1, 0x1f, 0x0c, 0xc9, 0x41, 0x13, 0x2e, 0x8c, 0xd5, - 0x22, 0x2e, 0xaa, 0x7f, 0x10, 0xab, 0xa9, 0x34, 0x99, 0x30, 0x46, 0x28, 0xe9, 0x2f, 0x8f, 0xbc, - 0x71, 0x2f, 0x7a, 0x50, 0x63, 0x27, 0xa0, 0x8f, 0x3b, 0xc8, 0xf3, 0x0e, 0x10, 0x3f, 0x43, 0x0f, - 0x6e, 0x55, 0x21, 0x2c, 0xa1, 0x52, 0x42, 0xea, 0xf7, 0x46, 0xde, 0x78, 0x2d, 0xda, 0xe5, 0xb7, - 0x88, 0x1c, 0xd5, 0x30, 0xfc, 0x05, 0xda, 0xc9, 0xb5, 0x2a, 0x05, 0x07, 0x4d, 0xa6, 0x00, 0x24, - 0x57, 0x2a, 0x25, 0x94, 0x73, 0x4d, 0x8c, 0xd5, 0xfe, 0x1d, 0x27, 0x72, 0xaf, 0x45, 0x3c, 0x06, - 0x98, 0x28, 0x95, 0x7e, 0xc9, 0xb9, 0x3e, 0xb3, 0x1a, 0x3f, 0x47, 0x98, 0xb1, 0x92, 0x58, 0x91, - 0x81, 0x2a, 0x6c, 0xe5, 0x4e, 0x28, 0xee, 0xbf, 0x35, 0xf2, 0xc6, 0x83, 0x83, 0xf7, 0x83, 0x3a, - 0xb1, 0x41, 0x9b, 0xd8, 0xe0, 0xb8, 0x49, 0xec, 0x61, 0xff, 0xe5, 0x1f, 0xbb, 0x4b, 0xbf, 0xfe, - 0xb9, 0xeb, 0x45, 0x77, 0x19, 0x2b, 0xcf, 0x6b, 0xf6, 0xc4, 0x91, 0xf1, 0xf7, 0xe8, 0x3d, 0xe7, - 0x66, 0x0a, 0xfa, 0x9f, 0xba, 0x2b, 0xaf, 0xaf, 0xfb, 0x6e, 0xab, 0xb1, 0x28, 0xfe, 0x04, 0x8d, - 0xda, 0x7b, 0x46, 0x34, 0x2c, 0xa4, 0x70, 0xaa, 0x29, 0xab, 0x3e, 0xfc, 0x55, 0xe7, 0x78, 0xd8, - 0xe2, 0xa2, 0x05, 0xd8, 0xe3, 0x06, 0x85, 0x1f, 0x22, 0x9c, 0x08, 0x63, 0x95, 0x16, 0x8c, 0xa6, - 0x04, 0xa4, 0xd5, 0x02, 0x8c, 0xdf, 0x77, 0x05, 0x7c, 0xfb, 0x66, 0xe7, 0xab, 0x7a, 0x03, 0x9f, - 0xa0, 0xbb, 0x85, 0x8c, 0x95, 0xe4, 0x42, 0xce, 0x5a, 0x3b, 0x6b, 0xaf, 0x6f, 0x67, 0x6b, 0x4e, - 0x6e, 0x8c, 0x7c, 0x8e, 0xee, 0x19, 0x35, 0xb5, 0x44, 0xe5, 0x96, 0x54, 0x19, 0xb2, 0x89, 0x06, - 0x93, 0xa8, 0x94, 0xfb, 0xa8, 0x3a, 0xfe, 0xe1, 0xb2, 0xef, 0x45, 0xef, 0x54, 0x88, 0xd3, 0xdc, - 0x9e, 0x16, 0xf6, 0xbc, 0xdd, 0xc6, 0x1f, 0xa0, 0x0d, 0x0d, 0x17, 0x54, 0x73, 0xc2, 0x41, 0xaa, - 0xcc, 0xf8, 0x83, 0x51, 0x6f, 0xbc, 0x16, 0xad, 0xd7, 0x8b, 0xc7, 0x6e, 0x0d, 0x3f, 0x42, 0xf3, - 0x82, 0x93, 0x45, 0xf4, 0xba, 0x43, 0x6f, 0xb7, 0xbb, 0x51, 0x97, 0xf5, 0x1c, 0x61, 0x0d, 0x56, - 0x5f, 0x12, 0x0e, 0x29, 0xbd, 0x6c, 0x5d, 0x6e, 0xbc, 0xc1, 0x65, 0x70, 0xf4, 0xe3, 0x8a, 0xdd, - 0xd8, 0xdc, 0x45, 0x83, 0x79, 0xbd, 0x04, 0xf7, 0x37, 0x5d, 0x69, 0x50, 0xbb, 0xf4, 0x94, 0xef, - 0xfd, 0xe6, 0xa1, 0xed, 0xb6, 0x0d, 0xbf, 0x06, 0x09, 0x46, 0x98, 0x33, 0x4b, 0x2d, 0xe0, 0x27, - 0x68, 0x25, 0x77, 0x6d, 0xe9, 0x7a, 0x71, 0x70, 0xf0, 0x51, 0x70, 0xfb, 0x40, 0x09, 0x16, 0x1b, - 0xf9, 0xf0, 0x4e, 0x75, 0xa2, 0xa8, 0xe1, 0xe3, 0x67, 0xa8, 0xdf, 0xda, 0x75, 0x0d, 0x3a, 0x38, - 0x18, 0xff, 0x97, 0xd6, 0xa4, 0xc1, 0x3e, 0x95, 0x53, 0xd5, 0x28, 0xcd, 0xf9, 0xf8, 0x3e, 0x5a, - 0x93, 0x70, 0x41, 0x1c, 0xd3, 0xf5, 0x67, 0x3f, 0xea, 0x4b, 0xb8, 0x38, 0xaa, 0xe2, 0xbd, 0x9f, - 0x97, 0xd1, 0x7a, 0x97, 0x8d, 0x4f, 0xd0, 0x7a, 0x3d, 0xc3, 0x88, 0xa9, 0x3c, 0x35, 0x4e, 0x3e, - 0x0e, 0x44, 0xcc, 0x82, 0xee, 0x84, 0x0b, 0x3a, 0x33, 0xad, 0x72, 0xe3, 0x56, 0x5d, 0x1a, 0xa2, - 0x01, 0xbb, 0x09, 0xf0, 0xb7, 0x68, 0xab, 0x4a, 0x1d, 0x48, 0x53, 0x98, 0x46, 0xb2, 0x36, 0x14, - 0xfc, 0xaf, 0x64, 0x4b, 0xab, 0x55, 0x37, 0xd9, 0x42, 0x8c, 0x4f, 0xd0, 0x96, 0x90, 0xc2, 0x0a, - 0x9a, 0x92, 0x92, 0xa6, 0xc4, 0x80, 0xf5, 0x7b, 0xa3, 0xde, 0x78, 0x70, 0x30, 0xea, 0xea, 0x54, - 0xa3, 0x3a, 0x78, 0x41, 0x53, 0xc1, 0xa9, 0x55, 0xfa, 0x9b, 0x9c, 0x53, 0x0b, 0x4d, 0x86, 0x36, - 0x1a, 0xfa, 0x0b, 0x9a, 0x9e, 0x81, 0x3d, 0x3c, 0x79, 0x79, 0x35, 0xf4, 0x5e, 0x5d, 0x0d, 0xbd, - 0xbf, 0xae, 0x86, 0xde, 0x2f, 0xd7, 0xc3, 0xa5, 0x57, 0xd7, 0xc3, 0xa5, 0xdf, 0xaf, 0x87, 0x4b, - 0xdf, 0x3d, 0x9a, 0x09, 0x9b, 0x14, 0x71, 0xc0, 0x54, 0x16, 0x32, 0x65, 0x32, 0x65, 0xc2, 0x9b, - 0x5a, 0x3c, 0x9c, 0x3f, 0x2d, 0xe5, 0x67, 0xe1, 0x8f, 0xee, 0x7d, 0x71, 0x2f, 0x43, 0xbc, 0xe2, - 0x6e, 0xdd, 0xa7, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0xbc, 0xf0, 0xde, 0x5e, 0x87, 0x06, 0x00, - 0x00, + // 864 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0x41, 0x6f, 0xe4, 0x34, + 0x14, 0x6e, 0x3a, 0xbb, 0xed, 0xd4, 0x33, 0x6d, 0x17, 0x53, 0x4a, 0xe8, 0x4a, 0xd3, 0xd9, 0xc2, + 0x61, 0x04, 0xda, 0x84, 0x96, 0x15, 0x48, 0xdc, 0xe8, 0x94, 0x65, 0xbb, 0x87, 0x76, 0x36, 0x2d, + 0x45, 0x82, 0x83, 0xe5, 0xd8, 0x6f, 0x66, 0x2c, 0x32, 0x76, 0x64, 0x3b, 0x29, 0xbd, 0x23, 0xb8, + 0x72, 0xe4, 0x27, 0xed, 0x71, 0x8f, 0x9c, 0x00, 0xb5, 0x7f, 0x04, 0xc5, 0x49, 0xa6, 0x19, 0x44, + 0xa1, 0xdc, 0xf2, 0x9e, 0xbf, 0xef, 0x8b, 0xdf, 0xf7, 0xfc, 0x6c, 0xf4, 0xb1, 0x90, 0x16, 0x34, + 0x9b, 0x52, 0x21, 0x89, 0x01, 0x96, 0x69, 0x61, 0xaf, 0x42, 0xc6, 0xf2, 0x30, 0xdf, 0x0f, 0xcd, + 0x94, 0x6a, 0xe0, 0x84, 0x29, 0x69, 0xb2, 0x19, 0xe8, 0x20, 0xd5, 0xca, 0x2a, 0xbc, 0xf3, 0x0f, + 0x8c, 0x80, 0xb1, 0x3c, 0xc8, 0xf7, 0x77, 0x1e, 0x5b, 0x90, 0x1c, 0xf4, 0x4c, 0x48, 0x1b, 0xd2, + 0x98, 0x89, 0xd0, 0x5e, 0xa5, 0x60, 0x4a, 0xe2, 0x4e, 0x28, 0x62, 0x16, 0x26, 0x62, 0x32, 0xb5, + 0x2c, 0x11, 0x20, 0xad, 0x09, 0x1b, 0xe8, 0x7c, 0xbf, 0x11, 0x55, 0x84, 0xde, 0x44, 0xa9, 0x49, + 0x02, 0xa1, 0x8b, 0xe2, 0x6c, 0x1c, 0xf2, 0x4c, 0x53, 0x2b, 0x94, 0xac, 0xd6, 0xb7, 0x26, 0x6a, + 0xa2, 0xdc, 0x67, 0x58, 0x7c, 0x95, 0xd9, 0xbd, 0x1f, 0x57, 0xd1, 0xc6, 0xb0, 0xda, 0xf2, 0x88, + 0x6a, 0x3a, 0x33, 0xd8, 0x47, 0xab, 0x20, 0x69, 0x9c, 0x00, 0xf7, 0xbd, 0xbe, 0x37, 0x68, 0x47, + 0x75, 0x88, 0x4f, 0xd1, 0x07, 0x71, 0xa2, 0xd8, 0xf7, 0x86, 0xa4, 0xa0, 0x09, 0x17, 0xc6, 0x6a, + 0x11, 0x67, 0xc5, 0x3f, 0x88, 0xd5, 0x54, 0x9a, 0x99, 0x30, 0x46, 0x28, 0xe9, 0x2f, 0xf7, 0xbd, + 0x41, 0x2b, 0x7a, 0x52, 0x62, 0x47, 0xa0, 0x8f, 0x1a, 0xc8, 0xf3, 0x06, 0x10, 0xbf, 0x44, 0x4f, + 0xee, 0x54, 0x21, 0x6c, 0x4a, 0xa5, 0x84, 0xc4, 0x6f, 0xf5, 0xbd, 0xc1, 0x5a, 0xb4, 0xcb, 0xef, + 0x10, 0x19, 0x96, 0x30, 0xfc, 0x39, 0xda, 0x49, 0xb5, 0xca, 0x05, 0x07, 0x4d, 0xc6, 0x00, 0x24, + 0x55, 0x2a, 0x21, 0x94, 0x73, 0x4d, 0x8c, 0xd5, 0xfe, 0x03, 0x27, 0xb2, 0x5d, 0x23, 0x9e, 0x03, + 0x8c, 0x94, 0x4a, 0xbe, 0xe0, 0x5c, 0x9f, 0x59, 0x8d, 0x5f, 0x21, 0xcc, 0x58, 0x4e, 0xac, 0x98, + 0x81, 0xca, 0x6c, 0x51, 0x9d, 0x50, 0xdc, 0x7f, 0xd8, 0xf7, 0x06, 0x9d, 0x83, 0xf7, 0x82, 0xd2, + 0xd8, 0xa0, 0x36, 0x36, 0x38, 0xaa, 0x8c, 0x3d, 0x6c, 0xbf, 0xfe, 0x7d, 0x77, 0xe9, 0xd7, 0x3f, + 0x76, 0xbd, 0xe8, 0x11, 0x63, 0xf9, 0x79, 0xc9, 0x1e, 0x39, 0x32, 0xfe, 0x0e, 0xbd, 0xeb, 0xaa, + 0x19, 0x83, 0xfe, 0xbb, 0xee, 0xca, 0xfd, 0x75, 0xdf, 0xa9, 0x35, 0x16, 0xc5, 0x5f, 0xa0, 0x7e, + 0x7d, 0xce, 0x88, 0x86, 0x05, 0x0b, 0xc7, 0x9a, 0xb2, 0xe2, 0xc3, 0x5f, 0x75, 0x15, 0xf7, 0x6a, + 0x5c, 0xb4, 0x00, 0x7b, 0x5e, 0xa1, 0xf0, 0x53, 0x84, 0xa7, 0xc2, 0x58, 0xa5, 0x05, 0xa3, 0x09, + 0x01, 0x69, 0xb5, 0x00, 0xe3, 0xb7, 0x5d, 0x03, 0xdf, 0xba, 0x5d, 0xf9, 0xb2, 0x5c, 0xc0, 0x27, + 0xe8, 0x51, 0x26, 0x63, 0x25, 0xb9, 0x90, 0x93, 0xba, 0x9c, 0xb5, 0xfb, 0x97, 0xb3, 0x39, 0x27, + 0x57, 0x85, 0x7c, 0x86, 0xb6, 0x8d, 0x1a, 0x5b, 0xa2, 0x52, 0x4b, 0x0a, 0x87, 0xec, 0x54, 0x83, + 0x99, 0xaa, 0x84, 0xfb, 0xa8, 0xd8, 0xfe, 0xe1, 0xb2, 0xef, 0x45, 0x6f, 0x17, 0x88, 0xd3, 0xd4, + 0x9e, 0x66, 0xf6, 0xbc, 0x5e, 0xc6, 0xef, 0xa3, 0x75, 0x0d, 0x97, 0x54, 0x73, 0xc2, 0x41, 0xaa, + 0x99, 0xf1, 0x3b, 0xfd, 0xd6, 0x60, 0x2d, 0xea, 0x96, 0xc9, 0x23, 0x97, 0xc3, 0xcf, 0xd0, 0xbc, + 0xe1, 0x64, 0x11, 0xdd, 0x75, 0xe8, 0xad, 0x7a, 0x35, 0x6a, 0xb2, 0x5e, 0x21, 0xac, 0xc1, 0xea, + 0x2b, 0xc2, 0x21, 0xa1, 0x57, 0x75, 0x95, 0xeb, 0xff, 0xe3, 0x30, 0x38, 0xfa, 0x51, 0xc1, 0xae, + 0xca, 0xdc, 0x45, 0x9d, 0x79, 0xbf, 0x04, 0xf7, 0x37, 0x5c, 0x6b, 0x50, 0x9d, 0x3a, 0xe6, 0x7b, + 0x3f, 0x2d, 0xa3, 0xad, 0x7a, 0x0c, 0xbf, 0x02, 0x09, 0x46, 0x98, 0x33, 0x4b, 0x2d, 0xe0, 0x17, + 0x68, 0x25, 0x75, 0x63, 0xe9, 0x66, 0xb1, 0x73, 0xf0, 0x61, 0x70, 0xf7, 0x85, 0x12, 0x2c, 0x0e, + 0xf2, 0xe1, 0x83, 0x62, 0x47, 0x51, 0xc5, 0xc7, 0x2f, 0x51, 0xbb, 0x2e, 0xd7, 0x0d, 0x68, 0xe7, + 0x60, 0xf0, 0x6f, 0x5a, 0xa3, 0x0a, 0x7b, 0x2c, 0xc7, 0xaa, 0x52, 0x9a, 0xf3, 0xf1, 0x63, 0xb4, + 0x26, 0xe1, 0x92, 0x38, 0xa6, 0x9b, 0xcf, 0x76, 0xd4, 0x96, 0x70, 0x39, 0x2c, 0x62, 0xbc, 0x8d, + 0x56, 0x52, 0x0d, 0xc3, 0xe1, 0x85, 0x1b, 0xba, 0x76, 0x54, 0x45, 0x45, 0xcb, 0x98, 0x92, 0x12, + 0xdc, 0xc1, 0x2b, 0x6c, 0x78, 0xe8, 0x6c, 0xe8, 0xde, 0x26, 0x8f, 0xf9, 0xde, 0xcf, 0xcb, 0xa8, + 0xdb, 0xfc, 0x35, 0x3e, 0x41, 0xdd, 0xf2, 0x02, 0x24, 0xa6, 0x30, 0xa4, 0xb2, 0xe1, 0xa3, 0x40, + 0xc4, 0x2c, 0x68, 0x5e, 0x8f, 0x41, 0xe3, 0x42, 0x2c, 0xac, 0x70, 0x59, 0xe7, 0x61, 0xd4, 0x61, + 0xb7, 0x01, 0xfe, 0x06, 0x6d, 0x16, 0xbe, 0x83, 0x34, 0x99, 0xa9, 0x24, 0x4b, 0x37, 0x82, 0xff, + 0x94, 0xac, 0x69, 0xa5, 0xea, 0x06, 0x5b, 0x88, 0xf1, 0x09, 0xda, 0x14, 0x52, 0x58, 0x41, 0x13, + 0x92, 0xd3, 0x84, 0x18, 0xb0, 0x7e, 0xab, 0xdf, 0x1a, 0x74, 0x0e, 0xfa, 0x4d, 0x9d, 0xe2, 0x9e, + 0x0f, 0x2e, 0x68, 0x22, 0x38, 0xb5, 0x4a, 0x7f, 0x9d, 0x72, 0x6a, 0xa1, 0xb2, 0x77, 0xbd, 0xa2, + 0x5f, 0xd0, 0xe4, 0x0c, 0xec, 0xe1, 0xc9, 0xeb, 0xeb, 0x9e, 0xf7, 0xe6, 0xba, 0xe7, 0xfd, 0x79, + 0xdd, 0xf3, 0x7e, 0xb9, 0xe9, 0x2d, 0xbd, 0xb9, 0xe9, 0x2d, 0xfd, 0x76, 0xd3, 0x5b, 0xfa, 0xf6, + 0xd9, 0x44, 0xd8, 0x69, 0x16, 0x07, 0x4c, 0xcd, 0x42, 0xa6, 0xcc, 0x4c, 0x99, 0xf0, 0xb6, 0x91, + 0x4f, 0xe7, 0xef, 0x52, 0xfe, 0x69, 0xf8, 0x83, 0x7b, 0x9c, 0xdc, 0xb3, 0x12, 0xaf, 0xb8, 0x23, + 0xfb, 0xc9, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xe3, 0xc1, 0x9c, 0xfa, 0xc4, 0x06, 0x00, 0x00, } func (m *ConsumerParams) Marshal() (dAtA []byte, err error) { @@ -555,6 +581,23 @@ func (m *ConsumerGenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ConnectionId) > 0 { + i -= len(m.ConnectionId) + copy(dAtA[i:], m.ConnectionId) + i = encodeVarintSharedConsumer(dAtA, i, uint64(len(m.ConnectionId))) + i-- + dAtA[i] = 0x2a + } + if m.PreCCV { + i-- + if m.PreCCV { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } if m.NewChain { i-- if m.NewChain { @@ -731,6 +774,13 @@ func (m *ConsumerGenesisState) Size() (n int) { if m.NewChain { n += 2 } + if m.PreCCV { + n += 2 + } + l = len(m.ConnectionId) + if l > 0 { + n += 1 + l + sovSharedConsumer(uint64(l)) + } return n } @@ -1342,6 +1392,58 @@ func (m *ConsumerGenesisState) Unmarshal(dAtA []byte) error { } } m.NewChain = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PreCCV", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSharedConsumer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.PreCCV = bool(v != 0) + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSharedConsumer + } + 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 ErrInvalidLengthSharedConsumer + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSharedConsumer + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConnectionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipSharedConsumer(dAtA[iNdEx:]) diff --git a/x/ccv/types/shared_params.go b/x/ccv/types/shared_params.go index 32b7019e23..7e2c2b86f9 100644 --- a/x/ccv/types/shared_params.go +++ b/x/ccv/types/shared_params.go @@ -82,6 +82,14 @@ func ValidateChannelIdentifier(i interface{}) error { return ibchost.ChannelIdentifierValidator(value) } +func ValidateConnectionIdentifier(connId string) error { + // accept empty string as valid + if strings.TrimSpace(connId) == "" { + return nil + } + return ibchost.ConnectionIdentifierValidator(connId) +} + func ValidateAccAddress(i interface{}) error { value, ok := i.(string) if !ok { @@ -109,6 +117,19 @@ func ValidateStringFraction(i interface{}) error { return nil } +func ValidateStringFractionNonZero(i interface{}) error { + if err := ValidateStringFraction(i); err != nil { + return err + } + str, _ := i.(string) + dec, _ := math.LegacyNewDecFromStr(str) + if dec.IsZero() { + return fmt.Errorf("param cannot be zero, got %s", str) + } + + return nil +} + func ValidateFraction(dec math.LegacyDec) error { if dec.IsNegative() { return fmt.Errorf("param cannot be negative, got %s", dec) diff --git a/x/ccv/types/shared_params_test.go b/x/ccv/types/shared_params_test.go index 9d2bbfce00..0b38f3f839 100644 --- a/x/ccv/types/shared_params_test.go +++ b/x/ccv/types/shared_params_test.go @@ -21,3 +21,51 @@ func TestValidateConsumerId(t *testing.T) { require.NoError(t, types.ValidateConsumerId("0")) require.NoError(t, types.ValidateConsumerId("18446744073709551615")) // 2^64 - 1 } + +func TestValidateConnectionIdentifier(t *testing.T) { + testCases := []struct { + name string + connId string + expPass bool + }{ + { + name: "valid connection ID", + connId: "connection-0", + expPass: true, + }, + { + name: "valid empty connection ID", + connId: "", + expPass: true, + }, + { + name: "valid empty (multiple spaces) connection ID", + connId: " ", + expPass: true, + }, + { + name: "invalid connection ID with /", + connId: "invalid-connection-id/", + expPass: false, + }, + { + name: "invalid connection ID with special characters", + connId: "connection-@#", + expPass: false, + }, + { + name: "invalid connection ID with spaces", + connId: "connection id", + expPass: false, + }, + } + + for _, tc := range testCases { + err := types.ValidateConnectionIdentifier(tc.connId) + if tc.expPass { + require.NoError(t, err, "valid case: '%s' should not return error. got %w", tc.name, err) + } else { + require.Error(t, err, "invalid case: '%s' must return error but got none", tc.name) + } + } +}