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
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).

View File

@ -139,7 +139,7 @@ func queryState() error {
// Create a connection to the gRPC server.
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.
)
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.
- 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.
- 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
::: 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.
@ -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:
```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
# 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
# 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
# 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
@ -154,7 +153,7 @@ At this point, `TxBuilder`'s underlying transaction is ready to be signed.
### 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`,
- 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"
)
func sendTx() error {
func sendTx(ctx context.Context) error {
// --snip--
// Create a connection to the gRPC server.
@ -269,7 +268,7 @@ func sendTx() error {
txClient := tx.NewServiceClient(grpcConn)
// We then call the BroadcastTx method on this client.
grpcRes, err := txClient.BroadcastTx(
context.Background(),
ctx,
&tx.BroadcastTxRequest{
Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC,
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;
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\""];
// 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\""];
// timestamp validator cannot be unjailed until
// Timestamp until which the validator is jailed due to liveness downtime.
google.protobuf.Timestamp jailed_until = 4
[(gogoproto.moretags) = "yaml:\"jailed_until\"", (gogoproto.stdtime) = true, (gogoproto.nullable) = false];
// whether or not a validator has been tombstoned (killed out of validator
// set)
// Whether or not a validator has been tombstoned (killed out of validator set). It is set
// once the validator commits an equivocation or for any other configured misbehiavor.
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\""];
}

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
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
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
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`.
It is indexed in the store as follows:
- ValidatorSigningInfo: ` 0x01 | ConsAddress -> amino(valSigningInfo)`
- MissedBlocksBitArray: ` 0x02 | ConsAddress | LittleEndianUint64(signArrayIndex) -> VarInt(didMiss)`
- ValidatorSigningInfo: ` 0x01 | ConsAddress -> ProtocolBuffer(ValSigningInfo)`
- 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
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
the block for a given index in the bit-array. The index in the bit-array is given
as little endian uint64.
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
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:
```protobuf
// 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.
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/slashing/v1beta1/slashing.proto#L11-L33

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)
if validator == nil
fail with "No validator found"
if getSelfDelegation(validator) == 0
fail with "validator must self delegate before unjailing"
if !validator.Jailed
fail with "Validator not jailed, cannot unjail"
@ -43,6 +46,6 @@ handleMsgUnjail(tx MsgUnjail)
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
provisions and rewards.

View File

@ -4,7 +4,18 @@ order: 5
# 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

View File

@ -6,7 +6,19 @@ order: 6
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 |
| ----- | ------------- | --------------------------- |
@ -23,12 +35,14 @@ The slashing module emits the following events/tags:
| liveness | missed_blocks | {missedBlocksCounter} |
| liveness | height | {blockHeight} |
## Handlers
### MsgUnjail
### Slash
| Type | Attribute Key | Attribute Value |
| ------- | ------------- | --------------- |
| message | module | slashing |
| message | action | unjail |
| message | sender | {senderAddress} |
+ same as `"slash"` event from `HandleValidatorSignature`, but without the `jailed` attribute.
### Jail
| 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),
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
"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).
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)
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
have to "re-earn" their delegations back. To put the validator in the tombstone
state, we set `DoubleSignJailEndTime` to `time.Unix(253402300800)`, the maximum
time supported by Amino.
have to "re-earn" their delegations back.
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

View File

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