Skip to content

Commit

Permalink
feat!: Customizable Slashing and Jailing (#2403)
Browse files Browse the repository at this point in the history
* Customizable Slashing and Jailing

* additional tests

* relay test fixes

* integration tests fix

* migrations

* slash with right params

* comment fix

* docs

* Revert "migrations"

This reverts commit f1e7c6a.

* cr fix

* updrade.md update

* cr fix

* Update testing documentation

* update changelog entries

---------

Co-authored-by: github-actions <[email protected]>
Co-authored-by: mpoke <[email protected]>
  • Loading branch information
3 people authored Dec 3, 2024
1 parent 331c1c9 commit 01f9698
Show file tree
Hide file tree
Showing 37 changed files with 2,258 additions and 532 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- `[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))
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- `[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))
50 changes: 48 additions & 2 deletions UPGRADING.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,46 @@
# Upgrading Replicated Security
# Upgrading Interchain Security

## Unreleased

### Provider

Upgrading a provider from v6.2.0 requires state migrations. The following migrators should be added to the upgrade handler of the provider chain:

```golang
// Initializes infraction parameters for each active consumer. During slashing and jailing of validators for misbehavior on the consumer chain, the parameters defined for that specific consumer will be used. Initially, default values are set, which can later be customized for each consumer as needed.
func SetConsumerInfractionParams(ctx sdk.Context, pk providerkeeper.Keeper) error {
infractionParameters := DefaultInfractionParams()

activeConsumerIds := pk.GetAllActiveConsumerIds(ctx)
for _, consumerId := range activeConsumerIds {
if err := pk.SetInfractionParameters(ctx, consumerId, infractionParameters); err != nil {
return err
}
}

return nil
}

func DefaultInfractionParams() providertypes.InfractionParameters {
return providertypes.InfractionParameters{
DoubleSign: &providertypes.SlashJailParameters{
JailDuration: time.Duration(1<<63 - 1), // the largest value a time.Duration can hold 9223372036854775807 (approximately 292 years)
SlashFraction: math.LegacyNewDecWithPrec(5, 2), // 0.05
},
Downtime: &providertypes.SlashJailParameters{
JailDuration: 600 * time.Second,
SlashFraction: math.LegacyNewDec(0), // no slashing for downtime on the consumer
},
}
}
```

## v6.3.x

Upgrading from `v6.2.0` will not require state migration. To upgrade from lower versions, please check the sections below.

## v6.2.x

### Consumer

Upgrading a consumer from v4.4.x to v4.5.x and from v5.x or v6.1.x to v6.2.x requires state migrations. The following migrators should be added to the upgrade handler of the consumer chain:
Expand All @@ -22,6 +61,13 @@ func InitializeConsumerId(ctx sdk.Context, consumerKeeper consumerkeeper.Keeper)
}
```

## [v6.1.x](https://github.com/cosmos/interchain-security/releases/tag/v6.1.0)

Upgrading from `v6.0.0` will not require state migration.


## [v6.0.x](https://github.com/cosmos/interchain-security/releases/tag/v6.0.0)

### Provider

Upgrading a provider from v5.1.x requires state migrations. The following migrators should be added to the upgrade handler of the provider chain:
Expand Down Expand Up @@ -378,4 +424,4 @@ Upgrading a provider from `v1.1.0-multiden` to `v2.0.0` will require state migra

### Consumer

Upgrading a consumer from `v1.2.0-multiden` to `v2.0.0` will NOT require state migrations.
Upgrading a consumer from `v1.2.0-multiden` to `v2.0.0` will NOT require state migrations.
34 changes: 31 additions & 3 deletions docs/docs/build/modules/02-provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -482,8 +482,8 @@ message MsgChangeRewardDenoms {
`MsgCreateConsumer` enables a user to create a consumer chain.

Both the `chain_id` and `metadata` fields are mandatory.
The `initialization_parameters`, `power_shaping_parameters`, and `allowlisted_reward_denoms` fields are optional.
The parameters not provided are set to their zero value.
The `initialization_parameters`, `power_shaping_parameters`, `infraction_parameters` and `allowlisted_reward_denoms` fields are optional.
The parameters not provided are set to their zero value. If `infraction_parameters` are not set, the default values currently configured on the provider are used.

The owner of the created consumer chain is the submitter of the message.
This message cannot be submitted as part of a governance proposal, i.e., the submitter cannot be the gov module account address.
Expand Down Expand Up @@ -516,6 +516,9 @@ message MsgCreateConsumer {
// allowlisted reward denoms by the consumer chain
AllowlistedRewardDenoms allowlisted_reward_denoms = 6;
// infraction parameters for slashing and jailing
InfractionParameters infraction_parameters = 7;
}
```

Expand All @@ -528,7 +531,7 @@ The others fields are optional. Not providing one of them will leave the existin
Providing one of `metadata`, `initialization_parameters`, `power_shaping_parameters`, or `allowlisted_reward_denoms`
will update all the containing fields.
If one of the containing fields is missing, it will be set to its zero value.
For example, updating the `initialization_parameters` without specifying the `spawn_time`, will set the `spawn_time` to zero.
For example, updating the `initialization_parameters` without specifying the `spawn_time`, will set the `spawn_time` to zero.

If the `initialization_parameters` field is set and `initialization_parameters.spawn_time > 0`, then the consumer chain will be scheduled to launch at `spawn_time`.
Updating the `spawn_time` from a positive value to zero will remove the consumer chain from the list of scheduled to launch chains.
Expand Down Expand Up @@ -568,6 +571,9 @@ message MsgUpdateConsumer {
// to update the chain id of the chain (can only be updated if the chain has not yet launched)
string new_chain_id = 8;
// infraction parameters for slashing and jailing
InfractionParameters infraction_parameters = 9;
}
```

Expand Down Expand Up @@ -829,6 +835,7 @@ In the `BeginBlock` of the provider module the following actions are performed:
- Remove every stopped consumer chain for which the removal time has passed.
- Replenish the throttling meter if necessary.
- Distribute ICS rewards to the opted in validators.
- Update consumer infraction parameters with the queued infraction parameters that were added to the queue before a time period greater than the unbonding time.

Note that for every consumer chain, the computation of its initial validator set is based on the consumer's [power shaping parameters](../../features/power-shaping.md)
and the [validators that opted in on that consumer](../../features/partial-set-security.md).
Expand Down Expand Up @@ -2874,6 +2881,16 @@ grpcurl -plaintext -d '{"consumer_id": "0"}' localhost:9090 interchain_security.
"minStake": "1000",
"allowInactiveVals": true
},
"infraction_parameters":{
"double_sign":{
"slash_fraction":"0.050000000000000000",
"jail_duration":"9223372036.854775807s"
},
"downtime":{
"slash_fraction":"0.000000000000000000",
"jail_duration":"600s"
}
},
"clientId": "07-tendermint-28"
}
```
Expand Down Expand Up @@ -3573,6 +3590,17 @@ Output:
"minStake": "1000",
"allowInactiveVals": true
}
,
"infraction_parameters":{
"double_sign":{
"slash_fraction":"0.050000000000000000",
"jail_duration":"9223372036.854775807s"
},
"downtime":{
"slash_fraction":"0.000000000000000000",
"jail_duration":"600s"
}
}
}
```

Expand Down
10 changes: 7 additions & 3 deletions docs/docs/features/slashing.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ The ICS protocol differentiates between downtime and equivocation infractions.
## Downtime Infractions

Downtime infractions are reported by consumer chains and are acted upon on the provider as soon as they are received.
Instead of slashing, the provider will **_only jail_** offending validator for the duration of time established by the provider chain parameters.
Note that validators are only jailed for downtime on consumer chains that they opted in to validate on,
The provider will jail and slash the offending validator. The jailing duration and slashing fraction are determined by the consumer's downtime infraction parameters on the provider chain.
By default, validators are **_only jailed_** for downtime on consumer chains that they opted in to validate on,
or in the case of Top N chains, where they are automatically opted in by being in the Top N% of the validator set on the provider.

For preventing malicious consumer chains from harming the provider, [slash throttling](../adrs/adr-002-throttle.md) (also known as _jail throttling_) ensures that only a fraction of the provider validator set can be jailed at any given time.
Expand All @@ -24,7 +24,7 @@ For preventing malicious consumer chains from harming the provider, [slash throt

Equivocation infractions are reported by external agents (e.g., relayers) that can submit to the provider evidence of light client or double signing attacks observed on a consumer chain.
The evidence is submitted by sending `MsgSubmitConsumerMisbehaviour` or `MsgSubmitConsumerDoubleVoting` messages to the provider.
When valid evidence is received, the malicious validators are slashed, jailed, and tombstoned on the provider.
When valid evidence is received, the malicious validators are slashed, jailed, and tombstoned on the provider. The jailing duration and slashing fraction are determined by the consumer's double sign infraction parameters on the provider chain.
This is enabled through the _cryptographic verification of equivocation_ feature.
For more details, see [ADR-005](../adrs/adr-005-cryptographic-equivocation-verification.md) and [ADR-013](../adrs/adr-013-equivocation-slashing.md).

Expand Down Expand Up @@ -597,3 +597,7 @@ The following command demonstrates how to run a Hermes instance in _evidence mod
hermes evidence --chain <CONSUMER-CHAIN-ID>
```
Note that `hermes evidence` takes a `--check-past-blocks` option giving the possibility to look for older evidence (default is 100).

### Infraction parameters

Jailing and slashing for misbehavior on a consumer chain are governed by parameters defined on the provider chain for that specific consumer chain. To create or update these infraction parameters, use the MsgCreateConsumer or MsgUpdateConsumer messages. When creating a consumer chain, if custom infraction parameters are not specified, default values from the provider are applied. For updates, parameters can be modified immediately if the chain is in the pre-launch phase. If the chain has already launched, the update will be scheduled to take effect after the unbonding period expires. This ensures that changes are applied seamlessly based on the chain's lifecycle.
19 changes: 19 additions & 0 deletions proto/interchain_security/ccv/provider/v1/provider.proto
Original file line number Diff line number Diff line change
Expand Up @@ -556,3 +556,22 @@ enum ConsumerPhase {
message AllowlistedRewardDenoms {
repeated string denoms = 1;
}

//
message InfractionParameters {
SlashJailParameters double_sign = 1;
SlashJailParameters downtime = 2;
}

//
message SlashJailParameters {
bytes slash_fraction = 1 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false,
(amino.dont_omitempty) = true
];
// for permanent jailing use 9223372036854775807 which is the largest value a time.Duration can hold (approximately 292 years)
google.protobuf.Duration jail_duration = 2
[ (gogoproto.nullable) = false, (gogoproto.stdduration) = true ];
}
5 changes: 4 additions & 1 deletion proto/interchain_security/ccv/provider/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ message Chain {
// filled with these validators first, and other validators will be added to the validator set only if there are
// not enough eligible priority validators.
repeated string prioritylist = 15;
// Infraction parameters for slashing and jailing
InfractionParameters infraction_parameters = 16;
}

message QueryValidatorConsumerAddrRequest {
Expand Down Expand Up @@ -397,9 +399,10 @@ message QueryConsumerChainResponse {
ConsumerMetadata metadata = 5 [ (gogoproto.nullable) = false ];
ConsumerInitializationParameters init_params = 6;
PowerShapingParameters power_shaping_params = 7;
InfractionParameters infraction_parameters = 8;

// corresponds to the id of the client that is created during launch
string client_id = 8;
string client_id = 9;
}

message QueryConsumerGenesisTimeRequest {
Expand Down
6 changes: 6 additions & 0 deletions proto/interchain_security/ccv/provider/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,9 @@ message MsgCreateConsumer {

// allowlisted reward denoms of the consumer
AllowlistedRewardDenoms allowlisted_reward_denoms = 6;

// infraction parameters for slashing and jailing
InfractionParameters infraction_parameters = 7;
}

// MsgCreateConsumerResponse defines response type for MsgCreateConsumer
Expand Down Expand Up @@ -399,6 +402,9 @@ message MsgUpdateConsumer {
// the chain id CANNOT be updated.
// This field is optional and can remain empty (i.e., `new_chain_id = ""`) or correspond to the chain id the chain already has.
string new_chain_id = 8;

// infraction parameters for slashing and jailing
InfractionParameters infraction_parameters = 9;
}

// MsgUpdateConsumerResponse defines response type for MsgUpdateConsumer messages
Expand Down
4 changes: 2 additions & 2 deletions scripts/test_doc/test_documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@
| Function | Short Description |
|----------|-------------------|
[TestHandleConsumerMisbehaviour](../../tests/integration/misbehaviour.go#L25) | TestHandleConsumerMisbehaviour tests the handling of consumer misbehavior.<details><summary>Details</summary>* Set up a CCV channel and send an empty VSC packet to ensure that the consumer client revision height is greater than 0.<br>* Construct a Misbehaviour object with two conflicting headers and process the equivocation evidence.<br>* Verify that the provider chain correctly processes this misbehavior.<br>* Ensure that all involved validators are jailed, tombstoned, and slashed according to the expected outcomes.<br>* Assert that their tokens are adjusted based on the slashing fraction.</details> |
[TestGetByzantineValidators](../../tests/integration/misbehaviour.go#L101) | TestGetByzantineValidators checks the GetByzantineValidators function on various instances of misbehaviour.<details><summary>Details</summary>* Set up a provider and consumer chain.<br>* Create a header with a subset of the validators on the consumer chain, then create a second header (in a variety of different ways),<br>and check which validators are considered Byzantine by calling the GetByzantineValidators function.<br>* The test scenarios are:<br>- when one of the headers is empty, the function should return an error<br>- when one of the headers has a corrupted validator set (e.g. by a validator having a different public key), the function should return an error<br>- when the signatures in one of the headers are corrupted, the function should return an error<br>- when the attack is an amnesia attack (i.e. the headers have different block IDs), no validator is considered byzantine<br>- for non-amnesia misbehaviour, all validators that signed both headers are considered byzantine</details> |
[TestCheckMisbehaviour](../../tests/integration/misbehaviour.go#L399) | TestCheckMisbehaviour tests that the CheckMisbehaviour function correctly checks for misbehaviour.<details><summary>Details</summary>* Set up a provider and consumer chain.<br>* Create a valid client header and then create a misbehaviour by creating a second header in a variety of different ways.<br>* Check that the CheckMisbehaviour function correctly checks for misbehaviour by verifying that<br>it returns an error when the misbehaviour is invalid and no error when the misbehaviour is valid.<br>* The test scenarios are:<br> - both headers are identical (returns an error)<br> - the misbehaviour is not for the consumer chain (returns an error)<br> - passing an invalid client id (returns an error)<br> - passing a misbehaviour with different header height (returns an error)<br> - passing a misbehaviour older than the min equivocation evidence height (returns an error)<br> - one header of the misbehaviour has insufficient voting power (returns an error)<br> - passing a valid misbehaviour (no error)<br><br>* Test does not test actually submitting the misbehaviour to the chain or freezing the client.</details> |
[TestGetByzantineValidators](../../tests/integration/misbehaviour.go#L102) | TestGetByzantineValidators checks the GetByzantineValidators function on various instances of misbehaviour.<details><summary>Details</summary>* Set up a provider and consumer chain.<br>* Create a header with a subset of the validators on the consumer chain, then create a second header (in a variety of different ways),<br>and check which validators are considered Byzantine by calling the GetByzantineValidators function.<br>* The test scenarios are:<br>- when one of the headers is empty, the function should return an error<br>- when one of the headers has a corrupted validator set (e.g. by a validator having a different public key), the function should return an error<br>- when the signatures in one of the headers are corrupted, the function should return an error<br>- when the attack is an amnesia attack (i.e. the headers have different block IDs), no validator is considered byzantine<br>- for non-amnesia misbehaviour, all validators that signed both headers are considered byzantine</details> |
[TestCheckMisbehaviour](../../tests/integration/misbehaviour.go#L400) | TestCheckMisbehaviour tests that the CheckMisbehaviour function correctly checks for misbehaviour.<details><summary>Details</summary>* Set up a provider and consumer chain.<br>* Create a valid client header and then create a misbehaviour by creating a second header in a variety of different ways.<br>* Check that the CheckMisbehaviour function correctly checks for misbehaviour by verifying that<br>it returns an error when the misbehaviour is invalid and no error when the misbehaviour is valid.<br>* The test scenarios are:<br> - both headers are identical (returns an error)<br> - the misbehaviour is not for the consumer chain (returns an error)<br> - passing an invalid client id (returns an error)<br> - passing a misbehaviour with different header height (returns an error)<br> - passing a misbehaviour older than the min equivocation evidence height (returns an error)<br> - one header of the misbehaviour has insufficient voting power (returns an error)<br> - passing a valid misbehaviour (no error)<br><br>* Test does not test actually submitting the misbehaviour to the chain or freezing the client.</details> |
</details>

# [normal_operations.go](../../tests/integration/normal_operations.go)
Expand Down
7 changes: 4 additions & 3 deletions tests/integration/double_vote.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,10 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() {

// verifies that the val gets slashed and has fewer tokens after the slashing
val, _ := s.providerApp.GetTestStakingKeeper().GetValidator(provCtx, provAddr.ToSdkConsAddr().Bytes())
slashFraction, err := s.providerApp.GetTestSlashingKeeper().SlashFractionDoubleSign(provCtx)
infractionParam, err := s.providerApp.GetProviderKeeper().GetInfractionParameters(provCtx, tc.consumerId)
s.Require().NoError(err)
actualTokens := math.LegacyNewDecFromInt(val.GetTokens())
s.Require().True(initialTokens.Sub(initialTokens.Mul(slashFraction)).Equal(actualTokens))
s.Require().True(initialTokens.Sub(initialTokens.Mul(infractionParam.DoubleSign.SlashFraction)).Equal(actualTokens))
} else {
s.Require().Error(err)

Expand Down Expand Up @@ -409,8 +409,9 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVotingSlashesUndelegationsAndRele
)
s.Require().NoError(err)

slashFraction, err := s.providerApp.GetTestSlashingKeeper().SlashFractionDoubleSign(s.providerCtx())
infractionParam, err := s.providerApp.GetProviderKeeper().GetInfractionParameters(s.providerCtx(), s.getFirstBundle().ConsumerId)
s.Require().NoError(err)
slashFraction := infractionParam.DoubleSign.SlashFraction

// check undelegations are slashed
ubds, _ = s.providerApp.GetTestStakingKeeper().GetUnbondingDelegation(s.providerCtx(), delAddr, valAddr)
Expand Down
Loading

0 comments on commit 01f9698

Please sign in to comment.