diff --git a/.changelog/epilogue.md b/.changelog/epilogue.md index ae4a370867..8d92e1734f 100644 --- a/.changelog/epilogue.md +++ b/.changelog/epilogue.md @@ -1,3 +1,26 @@ +## v4.5.0 + +*September 30, 2024* + +### BUG FIXES + +- Remove duplicate event emission on cached context. + ([\#2282](https://github.com/cosmos/interchain-security/pull/2282)) + +### FEATURES + +- `[x/consumer]` Populate the memo on the IBC transfer packets used to send ICS rewards +with the required consumer chain Id to identify the consumer to the provider. +- `[x/provider]` Identify the source of ICS rewards from the IBC transfer packet memo. + ([\#2290](https://github.com/cosmos/interchain-security/pull/2290)) + +### STATE BREAKING + +- `[x/consumer]` Populate the memo on the IBC transfer packets used to send ICS rewards +with the required consumer chain Id to identify the consumer to the provider. +- `[x/provider]` Identify the source of ICS rewards from the IBC transfer packet memo. + ([\#2290](https://github.com/cosmos/interchain-security/pull/2290)) + ## v4.4.0 *July 16, 2024* diff --git a/.changelog/unreleased/improvements/2357-init-params.md b/.changelog/unreleased/improvements/2357-init-params.md new file mode 100644 index 0000000000..392e659b49 --- /dev/null +++ b/.changelog/unreleased/improvements/2357-init-params.md @@ -0,0 +1,3 @@ +- `[x/provider]` Add validation for initial height and set + default values for consumer initialization params. + ([\#2357](https://github.com/cosmos/interchain-security/pull/2357)) \ No newline at end of file diff --git a/.changelog/unreleased/state-breaking/2357-init-params.md b/.changelog/unreleased/state-breaking/2357-init-params.md new file mode 100644 index 0000000000..392e659b49 --- /dev/null +++ b/.changelog/unreleased/state-breaking/2357-init-params.md @@ -0,0 +1,3 @@ +- `[x/provider]` Add validation for initial height and set + default values for consumer initialization params. + ([\#2357](https://github.com/cosmos/interchain-security/pull/2357)) \ No newline at end of file diff --git a/.changelog/unreleased/dependencies/2277-bump-ibc.md b/.changelog/v6.2.0/dependencies/2277-bump-ibc.md similarity index 100% rename from .changelog/unreleased/dependencies/2277-bump-ibc.md rename to .changelog/v6.2.0/dependencies/2277-bump-ibc.md diff --git a/.changelog/unreleased/features/2290-add-memo-to-ICS-rewards.md b/.changelog/v6.2.0/features/2290-add-memo-to-ICS-rewards.md similarity index 100% rename from .changelog/unreleased/features/2290-add-memo-to-ICS-rewards.md rename to .changelog/v6.2.0/features/2290-add-memo-to-ICS-rewards.md diff --git a/.changelog/unreleased/features/2309-allow-permissionless-whitelisting.md b/.changelog/v6.2.0/features/2309-allow-permissionless-whitelisting.md similarity index 100% rename from .changelog/unreleased/features/2309-allow-permissionless-whitelisting.md rename to .changelog/v6.2.0/features/2309-allow-permissionless-whitelisting.md diff --git a/.changelog/unreleased/state-breaking/2290-add-memo-to-ICS-rewards.md b/.changelog/v6.2.0/state-breaking/2290-add-memo-to-ICS-rewards.md similarity index 100% rename from .changelog/unreleased/state-breaking/2290-add-memo-to-ICS-rewards.md rename to .changelog/v6.2.0/state-breaking/2290-add-memo-to-ICS-rewards.md diff --git a/.changelog/unreleased/state-breaking/2309-allow-permissionless-whitelisting.md b/.changelog/v6.2.0/state-breaking/2309-allow-permissionless-whitelisting.md similarity index 100% rename from .changelog/unreleased/state-breaking/2309-allow-permissionless-whitelisting.md rename to .changelog/v6.2.0/state-breaking/2309-allow-permissionless-whitelisting.md diff --git a/.changelog/v6.2.0/summary.md b/.changelog/v6.2.0/summary.md new file mode 100644 index 0000000000..788d56b7cc --- /dev/null +++ b/.changelog/v6.2.0/summary.md @@ -0,0 +1,2 @@ +*October 4, 2024* + diff --git a/CHANGELOG.md b/CHANGELOG.md index f606aa3eeb..aa093f3aeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,33 @@ # CHANGELOG +## v6.2.0 + +*October 4, 2024* + +### DEPENDENCIES + +- Bump [ibc-go](https://github.com/cosmos/ibc-go) to + [v8.5.1](https://github.com/cosmos/ibc-go/releases/tag/v8.5.1). + ([\#2277](https://github.com/cosmos/interchain-security/pull/2277)) + +### FEATURES + +- `[x/consumer]` Populate the memo on the IBC transfer packets used to send ICS rewards. +with the required consumer chain Id to identify the consumer to the provider. +- `[x/provider]` Identify the source of ICS rewards from the IBC transfer packet memo. + ([\#2290](https://github.com/cosmos/interchain-security/pull/2290)) +- `[x/provider]` Enable permissionless allowlisting of reward denoms (at most 3) per consumer chain. + ([\#2309](https://github.com/cosmos/interchain-security/pull/2309)) + +### STATE BREAKING + +- `[x/consumer]` Populate the memo on the IBC transfer packets used to send ICS rewards. +with the required consumer chain Id to identify the consumer to the provider. +- `[x/provider]` Identify the source of ICS rewards from the IBC transfer packet memo. + ([\#2290](https://github.com/cosmos/interchain-security/pull/2290)) +- `[x/provider]` Enable permissionless allowlisting of reward denoms (at most 3) per consumer chain. + ([\#2309](https://github.com/cosmos/interchain-security/pull/2309)) + ## v6.1.0 *September 20, 2024* @@ -317,6 +345,29 @@ Backporting of ([\#1964](https://github.com/cosmos/interchain-security/pull/1964 - Revert `PutUnbondingOnHold` behavior to ICS@v1 ([\#1819](https://github.com/cosmos/interchain-security/pull/1819)) +## v4.5.0 + +*September 30, 2024* + +### BUG FIXES + +- Remove duplicate event emission on cached context. + ([\#2282](https://github.com/cosmos/interchain-security/pull/2282)) + +### FEATURES + +- `[x/consumer]` Populate the memo on the IBC transfer packets used to send ICS rewards +with the required consumer chain Id to identify the consumer to the provider. +- `[x/provider]` Identify the source of ICS rewards from the IBC transfer packet memo. + ([\#2290](https://github.com/cosmos/interchain-security/pull/2290)) + +### STATE BREAKING + +- `[x/consumer]` Populate the memo on the IBC transfer packets used to send ICS rewards +with the required consumer chain Id to identify the consumer to the provider. +- `[x/provider]` Identify the source of ICS rewards from the IBC transfer packet memo. + ([\#2290](https://github.com/cosmos/interchain-security/pull/2290)) + ## v4.4.0 *July 16, 2024* diff --git a/Dockerfile b/Dockerfile index c762216205..12ac2212fa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,8 +29,8 @@ RUN go mod tidy RUN make install # Get Hermes build -## TODO: import Hermes release from ghcr.io/informalsystems repository when -## a Hermes release contains the patch in +# TODO: import Hermes release from ghcr.io/informalsystems repository when +# a Hermes release contains the patch in # https://github.com/informalsystems/hermes/pull/4182 FROM --platform=linux/amd64 otacrew/hermes-ics:latest AS hermes-builder diff --git a/Dockerfile.combined b/Dockerfile.combined index 47a8fad86a..7bd09917b9 100644 --- a/Dockerfile.combined +++ b/Dockerfile.combined @@ -24,7 +24,10 @@ FROM --platform=linux/amd64 ${PROVIDER_IMAGE} AS provider FROM --platform=linux/amd64 ${CONSUMER_IMAGE} AS consumer # Get Hermes build -FROM --platform=linux/amd64 ghcr.io/informalsystems/hermes:1.10.2 AS hermes-builder +# TODO: import Hermes release from ghcr.io/informalsystems repository when +# a Hermes release contains the patch in +# https://github.com/informalsystems/hermes/pull/4182 +FROM --platform=linux/amd64 otacrew/hermes-ics:latest AS hermes-builder # Get GoRelayer diff --git a/docs/docs/build/modules/02-provider.md b/docs/docs/build/modules/02-provider.md index 7be041d7d4..974bac5f8e 100644 --- a/docs/docs/build/modules/02-provider.md +++ b/docs/docs/build/modules/02-provider.md @@ -451,7 +451,7 @@ message MsgUpdateParams { } ``` -### ChangeRewardDenomsProposal +### MsgChangeRewardDenoms `MsgChangeRewardDenoms` updates the list of whitelisted denoms accepted by the provider as ICS rewards. The list of accepted denoms is updated through a governance proposal where the signer is the gov module account address. @@ -1550,7 +1550,7 @@ power_shaping_params: allow_inactive_vals: false allowlist: [] denylist: [] - min_stake: "0" + min_stake: 0 top_N: 100 validator_set_cap: 0 validators_power_cap: 0 @@ -1642,7 +1642,7 @@ where `create-consumer-msg.json` contains: "metadata": { "name": "pion-1", "description":"description of your chain and all other relevant information", - "metadata": "some metadata about your chain" + "metadata": "{\"forge_json_url\": \"...\", \"stage\": \"mainnet\"}" } } ``` @@ -1678,31 +1678,31 @@ where `update-consumer-msg.json` contains: "metadata": { "name": "pion-1", "description":"description of your chain and all other relevant information", - "metadata": "some metadata about your" + "metadata": "{\"forge_json_url\": \"...\", \"stage\": \"mainnet\"}" }, "initialization_parameters":{ "initial_height":{ - "revision_number": 0, - "revision_height": 1 + "revision_number": 1, + "revision_height": 0 }, - "genesis_hash":"2D5C2110941DA54BE07CBB9FACD7E4A2E3253E79BE7BE3E5A1A7BDA518BAA4BE", - "binary_hash": "6EF05C2F38BE62A833E5AB51EBF3BA72D1BC1664D7E4A2E3253DA54BE07CF38A", + "genesis_hash": "", + "binary_hash": "", "spawn_time": "2024-09-29T12:57:43Z", - "unbonding_period":"2419200s", - "ccv_timeout_period": "2419200s", - "transfer_timeout_period": "3600s", + "unbonding_period": 1728000000000000, + "ccv_timeout_period": 2419200000000000, + "transfer_timeout_period": 1800000000000, "consumer_redistribution_fraction": "0.75", "blocks_per_distribution_transmission": "1500", - "historical_entries":"1000", + "historical_entries": "1000", "distribution_transmission_channel": "" }, "power_shaping_parameters":{ - "top_N":50, - "validators_power_cap":50, - "validator_set_cap":50, + "top_N": 0, + "validators_power_cap": 10, + "validator_set_cap": 50, "allowlist":["cosmosvalcons1l9qq4m300z8c5ez86ak2mp8znftewkwgjlxh88"], "denylist":[], - "min_stake": "1000", + "min_stake": 1000, "allow_inactive_vals":true }, "allowlisted_reward_denoms": { diff --git a/docs/docs/build/modules/03-consumer.md b/docs/docs/build/modules/03-consumer.md index 289ffb2bcb..47ffdbcd76 100644 --- a/docs/docs/build/modules/03-consumer.md +++ b/docs/docs/build/modules/03-consumer.md @@ -357,7 +357,7 @@ The consumer module contains the following parameters. Providing an IBC transfer channel enables a consumer chain to re-use one of the existing channels to the provider for consumer chain rewards distribution. This will preserve the `ibc denom` that may already be in use. This is especially important for standalone chains transitioning to become consumer chains. -For more details, see the [changeover procedure](../consumer-development/changeover-procedure.md). +For more details, see the [changeover procedure](../../consumer-development/changeover-procedure.md). ### ProviderFeePoolAddrStr @@ -439,7 +439,7 @@ It is recommended that every consumer chain set and unbonding period shorter tha | time.Duration | 3600s (1 hour) | `RetryDelayPeriod` is the period at which the consumer retries to send a `SlashPacket` that was rejected by the provider. -For more details, see [ADR-008](../adrs/adr-008-throttle-retries.md). +For more details, see [ADR-008](../../adrs/adr-008-throttle-retries.md). ## Client diff --git a/docs/docs/consumer-development/changeover-procedure.md b/docs/docs/consumer-development/changeover-procedure.md index d91664fdd4..240925fc0d 100644 --- a/docs/docs/consumer-development/changeover-procedure.md +++ b/docs/docs/consumer-development/changeover-procedure.md @@ -34,12 +34,11 @@ However, here are the most important notes and differences between a new consume // 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": 1, + "revision_height": 101, }, ... } ``` -RevisionNumber: 0, RevisionHeight: 111 ::: * `genesis_hash` can be safely ignored because the chain is already running. A hash of the standalone chain's initial genesis may be used @@ -177,10 +176,10 @@ Example of initialization parameters (compare with the [those](./onboarding.md# "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": 86400000000000, + "unbonding_period": 1728000000000000, // Timeout period for CCV related IBC packets. // Packets are considered timed-out after this interval elapses. - "ccv_timeout_period": 259200000000000, + "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. diff --git a/docs/docs/consumer-development/onboarding.md b/docs/docs/consumer-development/onboarding.md index eae1cf0208..4350db2c14 100644 --- a/docs/docs/consumer-development/onboarding.md +++ b/docs/docs/consumer-development/onboarding.md @@ -73,10 +73,10 @@ Example of initialization parameters: "spawn_time": "2023-02-28T20:40:00.000000Z", // Unbonding period for the consumer chain. // It should be smaller than that of the provider. - "unbonding_period": 86400000000000, + "unbonding_period": 1728000000000000, // Timeout period for CCV related IBC packets. // Packets are considered timed-out after this interval elapses. - "ccv_timeout_period": 259200000000000, + "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. @@ -108,7 +108,7 @@ Example of power-shaping parameters: // For example, 53 corresponds to a Top 53% chain, meaning that the top 53% provider validators by voting power // have to validate the proposed consumer chain. top_N can either be 0 or any value in [50, 100]. // A chain can join with top_N == 0 as an Opt In chain, or with top_N ∈ [50, 100] as a Top N chain. - "top_N": 95, + "top_N": 0, // Corresponds to the maximum power (percentage-wise) a validator can have on the consumer chain. For instance, if // `validators_power_cap` is set to 32, it means that no validator can have more than 32% of the voting power on the // consumer chain. Note that this might not be feasible. For example, think of a consumer chain with only @@ -120,7 +120,7 @@ Example of power-shaping parameters: "validator_set_cap": 0, // Corresponds to a list of provider consensus addresses of validators that are the ONLY ones that can validate // the consumer chain. - "allowlist": [], + "allowlist": ["cosmosvalcons1l9qq4m300z8c5ez86ak2mp8znftewkwgjlxh88"], // Corresponds to a list of provider consensus addresses of validators that CANNOT validate the consumer chain. "denylist": [], // Corresponds to the minimal amount of (provider chain) stake required to validate on the consumer chain. diff --git a/docs/docs/introduction/terminology.md b/docs/docs/introduction/terminology.md index 85f09234b8..43e67f4020 100644 --- a/docs/docs/introduction/terminology.md +++ b/docs/docs/introduction/terminology.md @@ -39,11 +39,6 @@ PSS allows for more flexible security tradeoffs than Replicated Security. [Permissionless ICS](../features/permissionless.md) is the latest version of ICS that allows to launch Opt In chains in a permissionless way (i.e., without requiring a governance proposal). -## Consumer Chain - -Chain that is secured by the validator set of the provider, instead of its own. -Interchain Security allows a subset of the provider chain's validator set to validate blocks on the consumer chain. - ## Standalone Chain Chain that is secured by its own validator set. This chain does not participate in Interchain Security. diff --git a/tests/e2e/actions.go b/tests/e2e/actions.go index 872d19ce6c..06bc94c6af 100644 --- a/tests/e2e/actions.go +++ b/tests/e2e/actions.go @@ -637,7 +637,7 @@ func (tr Chain) submitConsumerAdditionProposal( DistributionTransmissionChannel: action.DistributionChannel, } - consumerId := tr.CreateConsumer(providerChainCfg.ChainId, consumerChainCfg.ChainId, action.From, Metadata, nil, nil) + consumerId := tr.CreateConsumer(providerChainCfg.ChainId, consumerChainCfg.ChainId, action.From, Metadata, &initializationParameters, nil) authority := "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn" // Set the new created consumer-id on the chain's config consumerChainCfg.ConsumerId = consumerId diff --git a/testutil/keeper/unit_test_helpers.go b/testutil/keeper/unit_test_helpers.go index 9c967a739e..929ce7be30 100644 --- a/testutil/keeper/unit_test_helpers.go +++ b/testutil/keeper/unit_test_helpers.go @@ -223,7 +223,7 @@ func SetupForDeleteConsumerChain(t *testing.T, ctx sdk.Context, t.Helper() expectations := GetMocksForCreateConsumerClient(ctx, &mocks, - "chainID", clienttypes.NewHeight(4, 5)) + "chainID", clienttypes.NewHeight(0, 5)) expectations = append(expectations, GetMocksForSetConsumerChain(ctx, &mocks, "chainID")...) gomock.InOrder(expectations...) @@ -286,7 +286,7 @@ func GetTestConsumerMetadata() providertypes.ConsumerMetadata { func GetTestInitializationParameters() providertypes.ConsumerInitializationParameters { return providertypes.ConsumerInitializationParameters{ - InitialHeight: clienttypes.NewHeight(4, 5), + InitialHeight: clienttypes.NewHeight(0, 5), GenesisHash: []byte("gen_hash"), BinaryHash: []byte("bin_hash"), SpawnTime: time.Now().UTC(), @@ -323,7 +323,7 @@ func GetTestMsgUpdateConsumer() providertypes.MsgUpdateConsumer { func GetTestMsgConsumerAddition() providertypes.MsgConsumerAddition { return providertypes.MsgConsumerAddition{ ChainId: "a ChainId", - InitialHeight: clienttypes.NewHeight(4, 5), + InitialHeight: clienttypes.NewHeight(0, 5), GenesisHash: []byte(base64.StdEncoding.EncodeToString([]byte("gen_hash"))), BinaryHash: []byte(base64.StdEncoding.EncodeToString([]byte("bin_hash"))), SpawnTime: time.Now(), diff --git a/x/ccv/provider/client/cli/tx.go b/x/ccv/provider/client/cli/tx.go index a66684b58d..d1caeb1d6f 100644 --- a/x/ccv/provider/client/cli/tx.go +++ b/x/ccv/provider/client/cli/tx.go @@ -95,7 +95,7 @@ An IBC misbehaviour contains two conflicting IBC client headers, which are used The misbehaviour type definition can be found in the IBC client messages, see ibc-go/proto/ibc/core/client/v1/tx.proto. Example: -%s tx provider submit-consumer-misbehaviour [consumer-id] [path/to/misbehaviour.json] --from node0 --home ../node0 --chain-id $CID +%s tx provider submit-consumer-misbehaviour [consumer-id] [path/to/misbehaviour.json] `, version.AppName)), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { @@ -154,7 +154,7 @@ func NewSubmitConsumerDoubleVotingCmd() *cobra.Command { definition can be found in the IBC messages, see ibc-go/proto/ibc/lightclients/tendermint/v1/tendermint.proto. Example: -%s tx provider submit-consumer-double-voting [consumer-id] [path/to/evidence.json] [path/to/infraction_header.json] --from node0 --home ../node0 --chain-id $CID +%s tx provider submit-consumer-double-voting [consumer-id] [path/to/evidence.json] [path/to/infraction_header.json] `, version.AppName)), Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { @@ -221,23 +221,23 @@ Note that the one that signs this message is the owner of this consumer chain. T changed by updating the consumer chain. Example: -%s tx provider create-consumer [path/to/create_consumer.json] --from node0 --home ../node0 --chain-id $CID +%s tx provider create-consumer [path/to/create_consumer.json] where create_consumer.json has the following structure: { - "chain_id": "consu", + "chain_id": "consumer-1", "metadata": { "name": "chain consumer", "description": "description", - "metadata": "metadata" + "metadata": "{\"forge_json_url\": \"...\", \"stage\": \"mainnet\"}" }, "initialization_parameters": { "initial_height": { - "revision_number": 0, - "revision_height": 1 + "revision_number": 1, + "revision_height": 0 }, - "genesis_hash": "Z2VuX2hhc2g=", - "binary_hash": "YmluX2hhc2g=", + "genesis_hash": "", + "binary_hash": "", "spawn_time": "2024-08-29T12:26:16.529913Z", "unbonding_period": 1728000000000000, "ccv_timeout_period": 2419200000000000, @@ -248,12 +248,12 @@ where create_consumer.json has the following structure: "distribution_transmission_channel": "" }, "power_shaping_parameters": { - "top_N": 100, - "validators_power_cap": 0, + "top_N": 0, + "validators_power_cap": 10, "validator_set_cap": 0, - "allowlist": [], - "denylist": [], - "min_stake": "0", + "allowlist": ["cosmosvalcons..."], + "denylist": ["cosmosvalcons..."], + "min_stake": 0, "allow_inactive_vals": false }, "allowlisted_reward_denoms": { @@ -318,7 +318,7 @@ func NewUpdateConsumerCmd() *cobra.Command { Note that only the owner of the chain can initialize it. Example: -%s tx provider update-consumer [path/to/update_consumer.json] --from node0 --home ../node0 --chain-id $CID +%s tx provider update-consumer [path/to/update_consumer.json] where update_consumer.json has the following structure: { @@ -327,15 +327,15 @@ where update_consumer.json has the following structure: "metadata": { "name": "chain consumer", "description": "description", - "metadata": "metadata" + "metadata": "{\"forge_json_url\": \"...\", \"stage\": \"mainnet\"}" }, "initialization_parameters": { "initial_height": { - "revision_number": 0, - "revision_height": 1 + "revision_number": 1, + "revision_height": 0 }, - "genesis_hash": "Z2VuX2hhc2g=", - "binary_hash": "YmluX2hhc2g=", + "genesis_hash": "", + "binary_hash": "", "spawn_time": "2024-08-29T12:26:16.529913Z", "unbonding_period": 1728000000000000, "ccv_timeout_period": 2419200000000000, @@ -346,12 +346,12 @@ where update_consumer.json has the following structure: "distribution_transmission_channel": "" }, "power_shaping_parameters": { - "top_N": 100, - "validators_power_cap": 0, + "top_N": 0, + "validators_power_cap": 10, "validator_set_cap": 0, - "allowlist": [], - "denylist": [], - "min_stake": "0", + "allowlist": ["cosmosvalcons..."], + "denylist": ["cosmosvalcons..."], + "min_stake": 0, "allow_inactive_vals": false }, "allowlisted_reward_denoms": { @@ -421,7 +421,7 @@ func NewRemoveConsumerCmd() *cobra.Command { Long: strings.TrimSpace( fmt.Sprintf(`Removes (and stops) a consumer chain. Note that only the owner of the chain can remove it. Example: -%s tx provider remove-consumer [consumer-id] --from node0 --home ../node0 --chain-id $CID +%s tx provider remove-consumer [consumer-id] `, version.AppName)), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { @@ -552,7 +552,7 @@ func NewSetConsumerCommissionRateCmd() *cobra.Command { Long: strings.TrimSpace( fmt.Sprintf(`Note that the "commission-rate" argument is a fraction and should be in the range [0,1]. Example: - %s set-consumer-commission-rate 123 0.5 --from node0 --home ../node0`, + %s set-consumer-commission-rate 123 0.5`, version.AppName), ), Args: cobra.ExactArgs(2), diff --git a/x/ccv/provider/keeper/consumer_lifecycle_test.go b/x/ccv/provider/keeper/consumer_lifecycle_test.go index 6b258ae003..200c2b949f 100644 --- a/x/ccv/provider/keeper/consumer_lifecycle_test.go +++ b/x/ccv/provider/keeper/consumer_lifecycle_test.go @@ -56,7 +56,8 @@ func TestPrepareConsumerForLaunch(t *testing.T) { func TestInitializeConsumer(t *testing.T) { now := time.Now().UTC() - consumerId := "13" + consumerId := CONSUMER_ID + chainId := CONSUMER_CHAIN_ID testCases := []struct { name string @@ -69,6 +70,7 @@ func TestInitializeConsumer(t *testing.T) { spawnTime: now, setup: func(pk *providerkeeper.Keeper, ctx sdk.Context, spawnTime time.Time) { pk.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_REGISTERED) + pk.SetConsumerChainId(ctx, consumerId, chainId) err := pk.SetConsumerInitializationParameters(ctx, consumerId, providertypes.ConsumerInitializationParameters{ SpawnTime: spawnTime, @@ -89,6 +91,7 @@ func TestInitializeConsumer(t *testing.T) { spawnTime: now, setup: func(pk *providerkeeper.Keeper, ctx sdk.Context, spawnTime time.Time) { pk.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_LAUNCHED) + pk.SetConsumerChainId(ctx, consumerId, chainId) err := pk.SetConsumerInitializationParameters(ctx, consumerId, providertypes.ConsumerInitializationParameters{ SpawnTime: spawnTime, @@ -110,6 +113,7 @@ func TestInitializeConsumer(t *testing.T) { spawnTime: now, setup: func(pk *providerkeeper.Keeper, ctx sdk.Context, spawnTime time.Time) { pk.SetConsumerPhase(ctx, consumerId, providertypes.CONSUMER_PHASE_REGISTERED) + pk.SetConsumerChainId(ctx, consumerId, chainId) err := pk.SetConsumerInitializationParameters(ctx, consumerId, providertypes.ConsumerInitializationParameters{ SpawnTime: time.Time{}, @@ -150,7 +154,7 @@ func TestBeginBlockLaunchConsumers(t *testing.T) { initializationParameters := []providertypes.ConsumerInitializationParameters{ { - InitialHeight: clienttypes.NewHeight(3, 4), + InitialHeight: clienttypes.NewHeight(0, 4), GenesisHash: []byte{}, BinaryHash: []byte{}, SpawnTime: now.Add(-time.Hour * 2).UTC(), @@ -163,7 +167,7 @@ func TestBeginBlockLaunchConsumers(t *testing.T) { DistributionTransmissionChannel: "", }, { - InitialHeight: clienttypes.NewHeight(3, 4), + InitialHeight: clienttypes.NewHeight(0, 4), GenesisHash: []byte{}, BinaryHash: []byte{}, SpawnTime: now.Add(-time.Hour).UTC(), @@ -176,7 +180,7 @@ func TestBeginBlockLaunchConsumers(t *testing.T) { DistributionTransmissionChannel: "", }, { - InitialHeight: clienttypes.NewHeight(3, 4), + InitialHeight: clienttypes.NewHeight(0, 4), GenesisHash: []byte{}, BinaryHash: []byte{}, SpawnTime: now.Add(time.Hour).UTC(), @@ -189,7 +193,7 @@ func TestBeginBlockLaunchConsumers(t *testing.T) { DistributionTransmissionChannel: "", }, { - InitialHeight: clienttypes.NewHeight(3, 4), + InitialHeight: clienttypes.NewHeight(0, 4), GenesisHash: []byte{}, BinaryHash: []byte{}, SpawnTime: now.Add(-time.Hour).UTC(), @@ -202,7 +206,7 @@ func TestBeginBlockLaunchConsumers(t *testing.T) { DistributionTransmissionChannel: "", }, { - InitialHeight: clienttypes.NewHeight(3, 4), + InitialHeight: clienttypes.NewHeight(0, 4), GenesisHash: []byte{}, BinaryHash: []byte{}, SpawnTime: now.Add(-time.Minute).UTC(), @@ -283,11 +287,11 @@ func TestBeginBlockLaunchConsumers(t *testing.T) { // Expect genesis and client creation for only the first, second, and fifth chains (spawn time already passed and valid) expectedCalls := testkeeper.GetMocksForMakeConsumerGenesis(ctx, &mocks, time.Hour) - expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain0", clienttypes.NewHeight(3, 4))...) + expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain0", clienttypes.NewHeight(0, 4))...) expectedCalls = append(expectedCalls, testkeeper.GetMocksForMakeConsumerGenesis(ctx, &mocks, time.Hour)...) - expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain1", clienttypes.NewHeight(3, 4))...) + expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain1", clienttypes.NewHeight(0, 4))...) expectedCalls = append(expectedCalls, testkeeper.GetMocksForMakeConsumerGenesis(ctx, &mocks, time.Hour)...) - expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain3", clienttypes.NewHeight(3, 4))...) + expectedCalls = append(expectedCalls, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, "chain3", clienttypes.NewHeight(0, 4))...) gomock.InOrder(expectedCalls...) @@ -479,7 +483,7 @@ func TestCreateConsumerClient(t *testing.T) { // Valid client creation is asserted with mock expectations here gomock.InOrder( - testkeeper.GetMocksForCreateConsumerClient(ctx, mocks, CONSUMER_CHAIN_ID, clienttypes.NewHeight(4, 5))..., + testkeeper.GetMocksForCreateConsumerClient(ctx, mocks, CONSUMER_CHAIN_ID, clienttypes.NewHeight(0, 5))..., ) }, expClientCreated: true, @@ -774,7 +778,7 @@ func TestBeginBlockStopConsumers(t *testing.T) { chainId := chainIds[i] // A consumer chain is setup corresponding to each consumerId, making these mocks necessary expectations = append(expectations, testkeeper.GetMocksForCreateConsumerClient(ctx, &mocks, - chainId, clienttypes.NewHeight(2, 3))...) + chainId, clienttypes.NewHeight(0, 3))...) expectations = append(expectations, testkeeper.GetMocksForSetConsumerChain(ctx, &mocks, chainId)...) } // Only first two consumer chains should be stopped @@ -789,7 +793,7 @@ func TestBeginBlockStopConsumers(t *testing.T) { for i, consumerId := range consumerIds { // Setup a valid consumer chain for each consumerId initializationRecord := testkeeper.GetTestInitializationParameters() - initializationRecord.InitialHeight = clienttypes.NewHeight(2, 3) + initializationRecord.InitialHeight = clienttypes.NewHeight(0, 3) registrationRecord := testkeeper.GetTestConsumerMetadata() providerKeeper.SetConsumerChainId(ctx, consumerId, chainIds[i]) diff --git a/x/ccv/provider/keeper/distribution.go b/x/ccv/provider/keeper/distribution.go index 7c0bf02764..8b5a0571d7 100644 --- a/x/ccv/provider/keeper/distribution.go +++ b/x/ccv/provider/keeper/distribution.go @@ -139,6 +139,9 @@ func (k Keeper) DeleteConsumerRewardsAllocationByDenom(ctx sdk.Context, consumer // AllocateConsumerRewards allocates the given rewards to provider consumer chain with the given consumer id func (k Keeper) AllocateConsumerRewards(ctx sdk.Context, consumerId string, alloc types.ConsumerRewardsAllocation) (types.ConsumerRewardsAllocation, error) { + if alloc.Rewards.IsZero() { + return types.ConsumerRewardsAllocation{}, nil + } chainId, err := k.GetConsumerChainId(ctx, consumerId) if err != nil { @@ -268,10 +271,6 @@ func (k Keeper) AllocateConsumerRewards(ctx sdk.Context, consumerId string, allo // AllocateTokens performs rewards distribution to the community pool and validators // based on the Partial Set Security distribution specification. func (k Keeper) AllocateTokens(ctx sdk.Context) { - // return if there is no coins in the consumer rewards pool - if k.GetConsumerRewardsPool(ctx).IsZero() { - return - } // Iterate over all launched consumer chains. // To avoid large iterations over all the consumer IDs, iterate only over @@ -312,6 +311,7 @@ func (k Keeper) AllocateTokens(ctx sdk.Context) { ) continue } + err = k.SetConsumerRewardsAllocationByDenom(cachedCtx, consumerId, denom, remainingRewardDec) if err != nil { k.Logger(ctx).Error( @@ -321,6 +321,7 @@ func (k Keeper) AllocateTokens(ctx sdk.Context) { ) continue } + writeCache() } } diff --git a/x/ccv/provider/keeper/grpc_query_test.go b/x/ccv/provider/keeper/grpc_query_test.go index 6ac71d8d25..58e3b4c323 100644 --- a/x/ccv/provider/keeper/grpc_query_test.go +++ b/x/ccv/provider/keeper/grpc_query_test.go @@ -315,11 +315,15 @@ func TestQueryConsumerChainsValidatorHasToValidate(t *testing.T) { // set up some consumer chains for i := 0; i < consumerNum; i++ { - chainID := "consumer-" + strconv.Itoa(i) + revisionNumber := i + 1 + chainID := "consumer-" + strconv.Itoa(revisionNumber) metadata := types.ConsumerMetadata{Name: chainID} + initializationParameters := types.DefaultConsumerInitializationParameters() + initializationParameters.InitialHeight.RevisionNumber = uint64(revisionNumber) msg := types.MsgCreateConsumer{ - ChainId: chainID, - Metadata: metadata, + ChainId: chainID, + Metadata: metadata, + InitializationParameters: &initializationParameters, } resp, err := msgServer.CreateConsumer(ctx, &msg) require.NoError(t, err) @@ -538,7 +542,7 @@ func TestQueryConsumerChain(t *testing.T) { defer ctrl.Finish() consumerId := "0" - chainId := "consumer-1" + chainId := "consumer" req := types.QueryConsumerChainRequest{ ConsumerId: consumerId, @@ -647,11 +651,15 @@ func TestQueryConsumerChains(t *testing.T) { // create four consumer chains in different phase for i := 0; i < consumerNum; i++ { - chainID := "consumer-" + strconv.Itoa(i) + revisionNumber := i + 1 + chainID := "consumer-" + strconv.Itoa(revisionNumber) metadata := types.ConsumerMetadata{Name: chainID} + initializationParameters := types.DefaultConsumerInitializationParameters() + initializationParameters.InitialHeight.RevisionNumber = uint64(revisionNumber) msg := types.MsgCreateConsumer{ - ChainId: chainID, - Metadata: metadata, + ChainId: chainID, + Metadata: metadata, + InitializationParameters: &initializationParameters, } resp, err := msgServer.CreateConsumer(ctx, &msg) require.NoError(t, err) diff --git a/x/ccv/provider/keeper/msg_server.go b/x/ccv/provider/keeper/msg_server.go index a66dfd9135..89970c4b70 100644 --- a/x/ccv/provider/keeper/msg_server.go +++ b/x/ccv/provider/keeper/msg_server.go @@ -378,7 +378,7 @@ func (k msgServer) CreateConsumer(goCtx context.Context, msg *types.MsgCreateCon // initialization parameters are optional and hence could be nil; // in that case, set the default - initializationParameters := types.ConsumerInitializationParameters{} // default params + initializationParameters := types.DefaultConsumerInitializationParameters() // default params if msg.InitializationParameters != nil { initializationParameters = *msg.InitializationParameters } diff --git a/x/ccv/provider/keeper/msg_server_test.go b/x/ccv/provider/keeper/msg_server_test.go index d90fde30ce..7100f44079 100644 --- a/x/ccv/provider/keeper/msg_server_test.go +++ b/x/ccv/provider/keeper/msg_server_test.go @@ -84,7 +84,7 @@ func TestUpdateConsumer(t *testing.T) { // create a chain before updating it createConsumerResponse, err := msgServer.CreateConsumer(ctx, &providertypes.MsgCreateConsumer{ - Submitter: "submitter", ChainId: "chainId", + Submitter: "submitter", ChainId: "chainId-1", Metadata: providertypes.ConsumerMetadata{ Name: "name", Description: "description", @@ -113,6 +113,7 @@ func TestUpdateConsumer(t *testing.T) { } expectedInitializationParameters := testkeeper.GetTestInitializationParameters() + expectedInitializationParameters.InitialHeight.RevisionNumber = 1 expectedPowerShapingParameters := testkeeper.GetTestPowerShapingParameters() expectedOwnerAddress := "cosmos1dkas8mu4kyhl5jrh4nzvm65qz588hy9qcz08la" diff --git a/x/ccv/provider/keeper/permissionless.go b/x/ccv/provider/keeper/permissionless.go index 43504a483e..dc96a5a062 100644 --- a/x/ccv/provider/keeper/permissionless.go +++ b/x/ccv/provider/keeper/permissionless.go @@ -134,6 +134,14 @@ func (k Keeper) SetConsumerInitializationParameters(ctx sdk.Context, consumerId if err != nil { return fmt.Errorf("failed to marshal initialization parameters (%+v) for consumer id (%s): %w", parameters, consumerId, err) } + chainId, err := k.GetConsumerChainId(ctx, consumerId) + if err != nil { + return fmt.Errorf("failed to get consumer chain ID for consumer id (%s): %w", consumerId, err) + } + // validate that the initial height matches the chain ID + if err := types.ValidateInitialHeight(parameters.InitialHeight, chainId); err != nil { + return fmt.Errorf("invalid initial height for consumer id (%s): %w", consumerId, err) + } store.Set(types.ConsumerIdToInitializationParametersKey(consumerId), bz) return nil } diff --git a/x/ccv/provider/keeper/permissionless_test.go b/x/ccv/provider/keeper/permissionless_test.go index 145afb43f1..3039d7b282 100644 --- a/x/ccv/provider/keeper/permissionless_test.go +++ b/x/ccv/provider/keeper/permissionless_test.go @@ -150,14 +150,16 @@ func TestConsumerInitializationParameters(t *testing.T) { HistoricalEntries: 456, DistributionTransmissionChannel: "distribution_transmission_channel", } - providerKeeper.SetConsumerInitializationParameters(ctx, CONSUMER_ID, expectedInitializationParameters) + providerKeeper.SetConsumerChainId(ctx, CONSUMER_ID, "chain-1") + err = providerKeeper.SetConsumerInitializationParameters(ctx, CONSUMER_ID, expectedInitializationParameters) + require.NoError(t, err) actualInitializationParameters, err := providerKeeper.GetConsumerInitializationParameters(ctx, CONSUMER_ID) require.NoError(t, err) require.Equal(t, expectedInitializationParameters, actualInitializationParameters) // assert that overwriting the current initialization record works expectedInitializationParameters = providertypes.ConsumerInitializationParameters{ - InitialHeight: types.Height{RevisionNumber: 2, RevisionHeight: 3}, + InitialHeight: types.Height{RevisionNumber: 1, RevisionHeight: 3}, GenesisHash: []byte{2, 3}, BinaryHash: []byte{4, 5}, SpawnTime: time.Unix(2, 3).UTC(), diff --git a/x/ccv/provider/types/msg.go b/x/ccv/provider/types/msg.go index 05fa7c6ee1..acfdf1bd8b 100644 --- a/x/ccv/provider/types/msg.go +++ b/x/ccv/provider/types/msg.go @@ -3,9 +3,11 @@ package types import ( "encoding/json" "fmt" - "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" "strings" + "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" ibctmtypes "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" errorsmod "cosmossdk.io/errors" @@ -620,3 +622,11 @@ func validateProviderAddress(addr, signer string) error { return nil } + +func ValidateInitialHeight(initialHeight clienttypes.Height, chainID string) error { + revision := clienttypes.ParseChainID(chainID) + if initialHeight.RevisionNumber != revision { + return fmt.Errorf("chain ID (%s) doesn't match revision number (%d)", chainID, initialHeight.RevisionNumber) + } + return nil +} diff --git a/x/ccv/provider/types/msg_test.go b/x/ccv/provider/types/msg_test.go index 84b40c043e..b0f0f11102 100644 --- a/x/ccv/provider/types/msg_test.go +++ b/x/ccv/provider/types/msg_test.go @@ -647,3 +647,66 @@ func TestMsgAssignConsumerKeyValidateBasic(t *testing.T) { }) } } + +func TestValidateInitialHeight(t *testing.T) { + testCases := []struct { + name string + chainId string + initialHeight clienttypes.Height + expPass bool + }{ + { + name: "valid with revision number", + chainId: "chain-1", + initialHeight: clienttypes.Height{ + RevisionNumber: 1, + RevisionHeight: 0, + }, + expPass: true, + }, + { + name: "valid without revision number", + chainId: "chain", + initialHeight: clienttypes.Height{ + RevisionNumber: 0, + RevisionHeight: 0, + }, + expPass: true, + }, + { + name: "invalid without revision number", + chainId: "chain", + initialHeight: clienttypes.Height{ + RevisionNumber: 1, + RevisionHeight: 0, + }, + expPass: false, + }, + { + name: "invalid without revision number", + chainId: "chain-1", + initialHeight: clienttypes.Height{ + RevisionNumber: 0, + RevisionHeight: 0, + }, + expPass: false, + }, + { + name: "valid: evmos-like chain IDs", + chainId: "evmos_9001-2", + initialHeight: clienttypes.Height{ + RevisionNumber: 2, + RevisionHeight: 0, + }, + expPass: true, + }, + } + for _, tc := range testCases { + err := types.ValidateInitialHeight(tc.initialHeight, tc.chainId) + 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) + } + } +} diff --git a/x/ccv/provider/types/provider.go b/x/ccv/provider/types/provider.go new file mode 100644 index 0000000000..9fd3e32718 --- /dev/null +++ b/x/ccv/provider/types/provider.go @@ -0,0 +1,28 @@ +package types + +import ( + "time" + + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + + ccv "github.com/cosmos/interchain-security/v6/x/ccv/types" +) + +func DefaultConsumerInitializationParameters() ConsumerInitializationParameters { + return ConsumerInitializationParameters{ + InitialHeight: clienttypes.Height{ + RevisionNumber: 1, + RevisionHeight: 1, + }, + GenesisHash: []byte{}, + BinaryHash: []byte{}, + SpawnTime: time.Time{}, + UnbondingPeriod: ccv.DefaultConsumerUnbondingPeriod, + CcvTimeoutPeriod: ccv.DefaultCCVTimeoutPeriod, + TransferTimeoutPeriod: ccv.DefaultTransferTimeoutPeriod, + ConsumerRedistributionFraction: ccv.DefaultConsumerRedistributeFrac, + BlocksPerDistributionTransmission: ccv.DefaultBlocksPerDistributionTransmission, + HistoricalEntries: ccv.DefaultHistoricalEntries, + DistributionTransmissionChannel: "", + } +} diff --git a/x/ccv/types/params.go b/x/ccv/types/params.go index 2a29eeb086..03b9d3f979 100644 --- a/x/ccv/types/params.go +++ b/x/ccv/types/params.go @@ -34,10 +34,10 @@ const ( // (and for consistency with other protobuf schemas defined for ccv). DefaultHistoricalEntries = int64(stakingtypes.DefaultHistoricalEntries) - // In general, the default unbonding period on the consumer is one week less + // In general, the default unbonding period on the consumer is one day less // than the default unbonding period on the provider, where the provider uses // the staking module default. - DefaultConsumerUnbondingPeriod = stakingtypes.DefaultUnbondingTime - 7*24*time.Hour + DefaultConsumerUnbondingPeriod = stakingtypes.DefaultUnbondingTime - 24*time.Hour // Default retry delay period is 1 hour. DefaultRetryDelayPeriod = time.Hour