Robert/docs slashing (#8329)

* update slashing docs

* x/slashing docs general audit & cleanup

* Update x/slashing/spec/02_state.md

Co-authored-by: Amaury <amaury.martiny@protonmail.com>

* Update x/slashing/spec/05_hooks.md

Co-authored-by: Alessio Treglia <alessio@tendermint.com>

* use code insertion widget

* review update

* Update x/slashing/spec/05_hooks.md

Co-authored-by: Alessio Treglia <alessio@tendermint.com>

* update link and location for fee distribution spec

Co-authored-by: Amaury <amaury.martiny@protonmail.com>
Co-authored-by: Alessio Treglia <alessio@tendermint.com>
This commit is contained in:
Robert Zaremba 2021-01-16 00:03:08 +01:00 committed by GitHub
parent df95ca8826
commit d39c42c987
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 88 additions and 82 deletions

View File

@ -32,7 +32,7 @@ Some important information concerning all legacy REST endpoints:
## Migrating to New REST Endpoints ## Migrating to New REST Endpoints
Thanks to the Protocol Buffers migration in v0.40 we are able to take advantage of a vast number of gRPC tools and solutions. For most of the legacy REST endpoints, Cosmos SDK v0.40 provides new REST endpoints generated from [gRPC `Query` services](../building-modules/query-services.md) using [grpc-gateway](https://grpc-ecosystem.github.io/grpc-gateway/). We usually call them _gGPC-gateway REST endpoints_. Thanks to the Protocol Buffers migration in v0.40, we are able to take advantage of a vast number of gRPC tools and solutions. For most of the legacy REST endpoints, Cosmos SDK v0.40 provides new REST endpoints generated from [gRPC `Query` services](../building-modules/query-services.md) using [grpc-gateway](https://grpc-ecosystem.github.io/grpc-gateway/). We usually call them _gGPC-gateway REST endpoints_.
Some modules expose legacy `POST` endpoints to generate unsigned transactions for their `Msg`s. These `POST` endpoints have been removed. We recommend to use [service `Msg`s](../building-modules/msg-services.md) directly, and use Protobuf to do client-side transaction generation. A guide can be found [here](../run-node/txs.md). Some modules expose legacy `POST` endpoints to generate unsigned transactions for their `Msg`s. These `POST` endpoints have been removed. We recommend to use [service `Msg`s](../building-modules/msg-services.md) directly, and use Protobuf to do client-side transaction generation. A guide can be found [here](../run-node/txs.md).

View File

@ -139,7 +139,7 @@ func queryState() error {
// Create a connection to the gRPC server. // Create a connection to the gRPC server.
grpcConn := grpc.Dial( grpcConn := grpc.Dial(
"127.0.0.1:9090", // Or your gRPC server address. "127.0.0.1:9090", // your gRPC server address.
grpc.WithInsecure(), // The SDK doesn't support any transport security mechanism. grpc.WithInsecure(), // The SDK doesn't support any transport security mechanism.
) )
defer grpcConn.Close() defer grpcConn.Close()

View File

@ -18,7 +18,7 @@ will run the following steps:
- generate a transaction with one `Msg` (`x/bank`'s `MsgSend`), and print the generated transaction to the console. - generate a transaction with one `Msg` (`x/bank`'s `MsgSend`), and print the generated transaction to the console.
- ask the user for confirmation to send the transaction from the `$MY_VALIDATOR_ADDRESS` account. - ask the user for confirmation to send the transaction from the `$MY_VALIDATOR_ADDRESS` account.
- fetch `$MY_VALIDATOR_ADDRESS` in the keyring. This is possible because we have [set up the CLI's keyring](./keyring.md) in a previous step. - fetch `$MY_VALIDATOR_ADDRESS` from the keyring. This is possible because we have [set up the CLI's keyring](./keyring.md) in a previous step.
- sign the generated transaction with the keyring's account. - sign the generated transaction with the keyring's account.
- broadcast the signed transaction to the network. This is possible because the CLI connects to the node's Tendermint RPC endpoint. - broadcast the signed transaction to the network. This is possible because the CLI connects to the node's Tendermint RPC endpoint.
@ -52,7 +52,7 @@ Some useful flags to consider in the `tx sign` command:
#### Signing with Multiple Signers #### Signing with Multiple Signers
::: warning ::: warning
Please note that signing a transaction with multiple signers or with a multisig account, where at least one signer uses `SIGN_MODE_DIRECT`, is not possible as of yet. You may follow [this Github issue](https://github.com/cosmos/cosmos-sdk/issues/8141) for more info. Please note that signing a transaction with multiple signers or with a multisig account, where at least one signer uses `SIGN_MODE_DIRECT`, is not yet possible. You may follow [this Github issue](https://github.com/cosmos/cosmos-sdk/issues/8141) for more info.
::: :::
Signing with multiple signers is done with the `tx multisign` command. This command assumes that all signers use `SIGN_MODE_LEGACY_AMINO_JSON`. The flow is similar to the `tx sign` command flow, but instead of signing an unsigned transaction file, each signer signs the file signed by previous signer(s). The `tx multisign` command will append signatures to the existing transactions. It is important that signers sign the transaction **in the same order** as given by the transaction, which is retrievable using the `GetSigners()` method. Signing with multiple signers is done with the `tx multisign` command. This command assumes that all signers use `SIGN_MODE_LEGACY_AMINO_JSON`. The flow is similar to the `tx sign` command flow, but instead of signing an unsigned transaction file, each signer signs the file signed by previous signer(s). The `tx multisign` command will append signatures to the existing transactions. It is important that signers sign the transaction **in the same order** as given by the transaction, which is retrievable using the `GetSigners()` method.
@ -60,14 +60,13 @@ Signing with multiple signers is done with the `tx multisign` command. This comm
For example, starting with the `unsigned_tx.json`, and assuming the transaction has 4 signers, we would run: For example, starting with the `unsigned_tx.json`, and assuming the transaction has 4 signers, we would run:
```bash ```bash
# Let signer 1 sign the unsigned tx. # Let signer1 sign the unsigned tx.
simd tx multisignsign unsigned_tx.json signer_key_1 --chain-id my-test-chain --keyring-backend test > partial_tx_1.json simd tx multisignsign unsigned_tx.json signer_key_1 --chain-id my-test-chain --keyring-backend test > partial_tx_1.json
# Signer 2 appends their signature. # Now signer1 will send the partial_tx_1.json to the signer2.
# Signer2 appends their signature:
simd tx multisignsign partial_tx_1.json signer_key_2 --chain-id my-test-chain --keyring-backend test > partial_tx_2.json simd tx multisignsign partial_tx_1.json signer_key_2 --chain-id my-test-chain --keyring-backend test > partial_tx_2.json
# Signer 3 appends their signature. # Signer2 sends the partial_tx_2.json file to signer3, and signer3 can append his signature:
simd tx multisignsign partial_tx_2.json signer_key_3 --chain-id my-test-chain --keyring-backend test > partial_tx_3.json simd tx multisignsign partial_tx_2.json signer_key_3 --chain-id my-test-chain --keyring-backend test > partial_tx_3.json
# Signer 4 appends their signature. The final output is the fully signed tx.
simd tx multisignsign partial_tx_3.json signer_key_4 --chain-id my-test-chain --keyring-backend test > signed_tx.json
``` ```
### Broadcasting a Transaction ### Broadcasting a Transaction
@ -154,7 +153,7 @@ At this point, `TxBuilder`'s underlying transaction is ready to be signed.
### Signing a Transaction ### Signing a Transaction
We chose our encoding config to use Protobuf, which will use `SIGN_MODE_DIRECT` by default. As per [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/docs/architecture/adr-020-protobuf-transaction-encoding.md), each signer needs to sign the `SignerInfo`s of all other signers. This means that we need to perform two steps sequentially: We set encoding config to use Protobuf, which will use `SIGN_MODE_DIRECT` by default. As per [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/docs/architecture/adr-020-protobuf-transaction-encoding.md), each signer needs to sign the `SignerInfo`s of all other signers. This means that we need to perform two steps sequentially:
- for each signer, populate the signer's `SignerInfo` inside `TxBuilder`, - for each signer, populate the signer's `SignerInfo` inside `TxBuilder`,
- once all `SignerInfo`s are populated, for each signer, sign the `SignDoc` (the payload to be signed). - once all `SignerInfo`s are populated, for each signer, sign the `SignDoc` (the payload to be signed).
@ -254,7 +253,7 @@ import (
"github.com/cosmos/cosmos-sdk/types/tx" "github.com/cosmos/cosmos-sdk/types/tx"
) )
func sendTx() error { func sendTx(ctx context.Context) error {
// --snip-- // --snip--
// Create a connection to the gRPC server. // Create a connection to the gRPC server.
@ -269,7 +268,7 @@ func sendTx() error {
txClient := tx.NewServiceClient(grpcConn) txClient := tx.NewServiceClient(grpcConn)
// We then call the BroadcastTx method on this client. // We then call the BroadcastTx method on this client.
grpcRes, err := txClient.BroadcastTx( grpcRes, err := txClient.BroadcastTx(
context.Background(), ctx,
&tx.BroadcastTxRequest{ &tx.BroadcastTxRequest{
Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC,
TxBytes: txBytes, // Proto-binary of the signed transaction, see previous step. TxBytes: txBytes, // Proto-binary of the signed transaction, see previous step.

View File

@ -1,3 +0,0 @@
# Spec Proposals
- [F1 Fee Distribution](./f1-fee-distribution/f1_fee_distr.pdf)

View File

@ -15,17 +15,20 @@ message ValidatorSigningInfo {
option (gogoproto.goproto_stringer) = false; option (gogoproto.goproto_stringer) = false;
string address = 1; string address = 1;
// height at which validator was first a candidate OR was unjailed // Height at which validator was first a candidate OR was unjailed
int64 start_height = 2 [(gogoproto.moretags) = "yaml:\"start_height\""]; int64 start_height = 2 [(gogoproto.moretags) = "yaml:\"start_height\""];
// index offset into signed block bit array // Index which is incremented each time the validator was a bonded
// in a block and may have signed a precommit or not. This in conjunction with the
// `SignedBlocksWindow` param determines the index in the `MissedBlocksBitArray`.
int64 index_offset = 3 [(gogoproto.moretags) = "yaml:\"index_offset\""]; int64 index_offset = 3 [(gogoproto.moretags) = "yaml:\"index_offset\""];
// timestamp validator cannot be unjailed until // Timestamp until which the validator is jailed due to liveness downtime.
google.protobuf.Timestamp jailed_until = 4 google.protobuf.Timestamp jailed_until = 4
[(gogoproto.moretags) = "yaml:\"jailed_until\"", (gogoproto.stdtime) = true, (gogoproto.nullable) = false]; [(gogoproto.moretags) = "yaml:\"jailed_until\"", (gogoproto.stdtime) = true, (gogoproto.nullable) = false];
// whether or not a validator has been tombstoned (killed out of validator // Whether or not a validator has been tombstoned (killed out of validator set). It is set
// set) // once the validator commits an equivocation or for any other configured misbehiavor.
bool tombstoned = 5; bool tombstoned = 5;
// missed blocks counter (to avoid scanning the array every time) // A counter kept to avoid unnecessary array reads.
// Note that `Sum(MissedBlocksBitArray)` always equals `MissedBlocksCounter`.
int64 missed_blocks_counter = 6 [(gogoproto.moretags) = "yaml:\"missed_blocks_counter\""]; int64 missed_blocks_counter = 6 [(gogoproto.moretags) = "yaml:\"missed_blocks_counter\""];
} }

View File

@ -10,9 +10,16 @@ Every block includes a set of precommits by the validators for the previous bloc
known as the `LastCommitInfo` provided by Tendermint. A `LastCommitInfo` is valid so known as the `LastCommitInfo` provided by Tendermint. A `LastCommitInfo` is valid so
long as it contains precommits from +2/3 of total voting power. long as it contains precommits from +2/3 of total voting power.
Proposers are incentivized to include precommits from all validators in the `LastCommitInfo` Proposers are incentivized to include precommits from all validators in the Tendermint `LastCommitInfo`
by receiving additional fees proportional to the difference between the voting by receiving additional fees proportional to the difference between the voting
power included in the `LastCommitInfo` and +2/3 (see [TODO](https://github.com/cosmos/cosmos-sdk/issues/967)). power included in the `LastCommitInfo` and +2/3 (see [fee distribution](https://github.com/cosmos/cosmos-sdk/blob/master/docs/spec/fee_distribution)).
```
type LastCommitInfo struct {
Round int32
Votes []VoteInfo
}
```
Validators are penalized for failing to be included in the `LastCommitInfo` for some Validators are penalized for failing to be included in the `LastCommitInfo` for some
number of blocks by being automatically jailed, potentially slashed, and unbonded. number of blocks by being automatically jailed, potentially slashed, and unbonded.
@ -20,15 +27,16 @@ number of blocks by being automatically jailed, potentially slashed, and unbonde
Information about validator's liveness activity is tracked through `ValidatorSigningInfo`. Information about validator's liveness activity is tracked through `ValidatorSigningInfo`.
It is indexed in the store as follows: It is indexed in the store as follows:
- ValidatorSigningInfo: ` 0x01 | ConsAddress -> amino(valSigningInfo)` - ValidatorSigningInfo: ` 0x01 | ConsAddress -> ProtocolBuffer(ValSigningInfo)`
- MissedBlocksBitArray: ` 0x02 | ConsAddress | LittleEndianUint64(signArrayIndex) -> VarInt(didMiss)` - MissedBlocksBitArray: ` 0x02 | ConsAddress | LittleEndianUint64(signArrayIndex) -> VarInt(didMiss)` (varint is a number encoding format)
The first mapping allows us to easily lookup the recent signing info for a The first mapping allows us to easily lookup the recent signing info for a
validator based on the validator's consensus address. The second mapping acts validator based on the validator's consensus address.
The second mapping (`MissedBlocksBitArray`) acts
as a bit-array of size `SignedBlocksWindow` that tells us if the validator missed as a bit-array of size `SignedBlocksWindow` that tells us if the validator missed
the block for a given index in the bit-array. The index in the bit-array is given the block for a given index in the bit-array. The index in the bit-array is given
as little endian uint64. as little endian uint64.
The result is a `varint` that takes on `0` or `1`, where `0` indicates the The result is a `varint` that takes on `0` or `1`, where `0` indicates the
validator did not miss (did sign) the corresponding block, and `1` indicates validator did not miss (did sign) the corresponding block, and `1` indicates
they missed the block (did not sign). they missed the block (did not sign).
@ -40,35 +48,4 @@ bonded validator. The `SignedBlocksWindow` parameter defines the size
The information stored for tracking validator liveness is as follows: The information stored for tracking validator liveness is as follows:
```protobuf +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/slashing/v1beta1/slashing.proto#L11-L33
// ValidatorSigningInfo defines a validator's signing info for monitoring their
// liveness activity.
message ValidatorSigningInfo {
string address = 1;
// height at which validator was first a candidate OR was unjailed
int64 start_height = 2;
// index offset into signed block bit array
int64 index_offset = 3;
// timestamp validator cannot be unjailed until
google.protobuf.Timestamp jailed_until = 4;
// whether or not a validator has been tombstoned (killed out of validator
// set)
bool tombstoned = 5;
// missed blocks counter (to avoid scanning the array every time)
int64 missed_blocks_counter = 6;
}
```
Where:
- **Address**: The validator's consensus address.
- **StartHeight**: The height that the candidate became an active validator
(with non-zero voting power).
- **IndexOffset**: Index which is incremented each time the validator was a bonded
in a block and may have signed a precommit or not. This in conjunction with the
`SignedBlocksWindow` param determines the index in the `MissedBlocksBitArray`.
- **JailedUntil**: Time for which the validator is jailed until due to liveness downtime.
- **Tombstoned**: Desribes if the validator is tombstoned or not. It is set once the
validator commits an equivocation or for any other configured misbehiavor.
- **MissedBlocksCounter**: A counter kept to avoid unnecessary array reads. Note
that `Sum(MissedBlocksBitArray)` equals `MissedBlocksCounter` always.

View File

@ -20,14 +20,17 @@ message MsgUnjail {
} }
``` ```
And below is its corresponding handler: Below is a pseudocode of the `MsgSrv/Unjail` RPC:
``` ```
handleMsgUnjail(tx MsgUnjail) unjail(tx MsgUnjail)
validator = getValidator(tx.ValidatorAddr) validator = getValidator(tx.ValidatorAddr)
if validator == nil if validator == nil
fail with "No validator found" fail with "No validator found"
if getSelfDelegation(validator) == 0
fail with "validator must self delegate before unjailing"
if !validator.Jailed if !validator.Jailed
fail with "Validator not jailed, cannot unjail" fail with "Validator not jailed, cannot unjail"
@ -43,6 +46,6 @@ handleMsgUnjail(tx MsgUnjail)
return return
``` ```
If the validator has enough stake to be in the top `n = MaximumBondedValidators`, they will be automatically rebonded, If the validator has enough stake to be in the top `n = MaximumBondedValidators`, it will be automatically rebonded,
and all delegators still delegated to the validator will be rebonded and begin to again collect and all delegators still delegated to the validator will be rebonded and begin to again collect
provisions and rewards. provisions and rewards.

View File

@ -4,7 +4,18 @@ order: 5
# Hooks # Hooks
In this section we describe the "hooks" - slashing module code that runs when other events happen. This section contains a description of the module's `hooks`. Hooks are operations that are executed automatically when events are raised.
## Staking hooks
The slashing module implements the `StakingHooks` defined in `x/staking` and are used as record-keeping of validators information. During the app initialization, these hooks should be registered in the staking module struct.
The following hooks impact the slashing state:
+ `AfterValidatorBonded` creates a `ValidatorSigningInfo` instance as described in the following section.
+ `AfterValidatorCreated` stores a validator's consensus key.
+ `AfterValidatorRemoved` removes a validator's consensus key.
## Validator Bonded ## Validator Bonded

View File

@ -6,7 +6,19 @@ order: 6
The slashing module emits the following events/tags: The slashing module emits the following events/tags:
## BeginBlocker ## MsgServer
### MsgUnjail
| Type | Attribute Key | Attribute Value |
| ------- | ------------- | --------------- |
| message | module | slashing |
| message | sender | {validatorAddress} |
## Keeper
## BeginBlocker: HandleValidatorSignature
| Type | Attribute Key | Attribute Value | | Type | Attribute Key | Attribute Value |
| ----- | ------------- | --------------------------- | | ----- | ------------- | --------------------------- |
@ -23,12 +35,14 @@ The slashing module emits the following events/tags:
| liveness | missed_blocks | {missedBlocksCounter} | | liveness | missed_blocks | {missedBlocksCounter} |
| liveness | height | {blockHeight} | | liveness | height | {blockHeight} |
## Handlers
### MsgUnjail ### Slash
| Type | Attribute Key | Attribute Value | + same as `"slash"` event from `HandleValidatorSignature`, but without the `jailed` attribute.
| ------- | ------------- | --------------- |
| message | module | slashing | ### Jail
| message | action | unjail |
| message | sender | {senderAddress} |
| Type | Attribute Key | Attribute Value |
| ----- | ------------- | ------------------ |
| slash | jailed | {validatorAddress} |

View File

@ -87,15 +87,17 @@ Currently, in the jail period implementation, once a validator unjails, all of
their delegators who are delegated to them (haven't unbonded / redelegated away), their delegators who are delegated to them (haven't unbonded / redelegated away),
stay with them. Given that consensus safety faults are so egregious stay with them. Given that consensus safety faults are so egregious
(way more so than liveness faults), it is probably prudent to have delegators not (way more so than liveness faults), it is probably prudent to have delegators not
"auto-rebond" to the validator. Thus, we propose setting the "jail time" for a "auto-rebond" to the validator.
### Proposal: infinite jail
We propose setting the "jail time" for a
validator who commits a consensus safety fault, to `infinite` (i.e. a tombstone state). validator who commits a consensus safety fault, to `infinite` (i.e. a tombstone state).
This essentially kicks the validator out of the validator set and does not allow This essentially kicks the validator out of the validator set and does not allow
them to re-enter the validator set. All of their delegators (including the operator themselves) them to re-enter the validator set. All of their delegators (including the operator themselves)
have to either unbond or redelegate away. The validator operator can create a new have to either unbond or redelegate away. The validator operator can create a new
validator if they would like, with a new operator key and consensus key, but they validator if they would like, with a new operator key and consensus key, but they
have to "re-earn" their delegations back. To put the validator in the tombstone have to "re-earn" their delegations back.
state, we set `DoubleSignJailEndTime` to `time.Unix(253402300800)`, the maximum
time supported by Amino.
Implementing the tombstone system and getting rid of the slashing period tracking Implementing the tombstone system and getting rid of the slashing period tracking
will make the `slashing` module way simpler, especially because we can remove all will make the `slashing` module way simpler, especially because we can remove all

View File

@ -6,10 +6,10 @@ order: 8
The slashing module contains the following parameters: The slashing module contains the following parameters:
| Key | Type | Example | | Key | Type | Example |
| ----------------------- | ---------------- | ---------------------- | | ----------------------- | -------------- | ---------------------- |
| SignedBlocksWindow | string (int64) | "100" | | SignedBlocksWindow | string (int64) | "100" |
| MinSignedPerWindow | string (dec) | "0.500000000000000000" | | MinSignedPerWindow | string (dec) | "0.500000000000000000" |
| DowntimeJailDuration | string (time ns) | "600000000000" | | DowntimeJailDuration | string (ns) | "600000000000" |
| SlashFractionDoubleSign | string (dec) | "0.050000000000000000" | | SlashFractionDoubleSign | string (dec) | "0.050000000000000000" |
| SlashFractionDowntime | string (dec) | "0.010000000000000000" | | SlashFractionDowntime | string (dec) | "0.010000000000000000" |