From d39c42c987738d4dd7bd89c35563eb1a32389183 Mon Sep 17 00:00:00 2001 From: Robert Zaremba Date: Sat, 16 Jan 2021 00:03:08 +0100 Subject: [PATCH] Robert/docs slashing (#8329) * update slashing docs * x/slashing docs general audit & cleanup * Update x/slashing/spec/02_state.md Co-authored-by: Amaury * Update x/slashing/spec/05_hooks.md Co-authored-by: Alessio Treglia * use code insertion widget * review update * Update x/slashing/spec/05_hooks.md Co-authored-by: Alessio Treglia * update link and location for fee distribution spec Co-authored-by: Amaury Co-authored-by: Alessio Treglia --- docs/migrations/rest.md | 2 +- docs/run-node/interact-node.md | 2 +- docs/run-node/txs.md | 19 +++---- docs/spec/_proposals/README.md | 3 - .../f1_fee_distr.pdf | Bin .../f1_fee_distr.tex | 0 proto/cosmos/slashing/v1beta1/slashing.proto | 15 +++-- x/slashing/spec/02_state.md | 53 +++++------------- x/slashing/spec/03_messages.md | 9 ++- x/slashing/spec/05_hooks.md | 13 ++++- x/slashing/spec/06_events.md | 30 +++++++--- x/slashing/spec/07_tombstone.md | 10 ++-- x/slashing/spec/08_params.md | 14 ++--- 13 files changed, 88 insertions(+), 82 deletions(-) delete mode 100644 docs/spec/_proposals/README.md rename docs/spec/{_proposals/f1-fee-distribution => fee_distribution}/f1_fee_distr.pdf (100%) rename docs/spec/{_proposals/f1-fee-distribution => fee_distribution}/f1_fee_distr.tex (100%) diff --git a/docs/migrations/rest.md b/docs/migrations/rest.md index 3af7e8ee2..b1675543e 100644 --- a/docs/migrations/rest.md +++ b/docs/migrations/rest.md @@ -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). diff --git a/docs/run-node/interact-node.md b/docs/run-node/interact-node.md index 60ce6c2ed..c7b30c57b 100644 --- a/docs/run-node/interact-node.md +++ b/docs/run-node/interact-node.md @@ -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() diff --git a/docs/run-node/txs.md b/docs/run-node/txs.md index 0c4c9de47..9fbc642ef 100644 --- a/docs/run-node/txs.md +++ b/docs/run-node/txs.md @@ -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. diff --git a/docs/spec/_proposals/README.md b/docs/spec/_proposals/README.md deleted file mode 100644 index 213d6288c..000000000 --- a/docs/spec/_proposals/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Spec Proposals - -- [F1 Fee Distribution](./f1-fee-distribution/f1_fee_distr.pdf) \ No newline at end of file diff --git a/docs/spec/_proposals/f1-fee-distribution/f1_fee_distr.pdf b/docs/spec/fee_distribution/f1_fee_distr.pdf similarity index 100% rename from docs/spec/_proposals/f1-fee-distribution/f1_fee_distr.pdf rename to docs/spec/fee_distribution/f1_fee_distr.pdf diff --git a/docs/spec/_proposals/f1-fee-distribution/f1_fee_distr.tex b/docs/spec/fee_distribution/f1_fee_distr.tex similarity index 100% rename from docs/spec/_proposals/f1-fee-distribution/f1_fee_distr.tex rename to docs/spec/fee_distribution/f1_fee_distr.tex diff --git a/proto/cosmos/slashing/v1beta1/slashing.proto b/proto/cosmos/slashing/v1beta1/slashing.proto index 657a90f1b..882a0fb60 100644 --- a/proto/cosmos/slashing/v1beta1/slashing.proto +++ b/proto/cosmos/slashing/v1beta1/slashing.proto @@ -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\""]; } diff --git a/x/slashing/spec/02_state.md b/x/slashing/spec/02_state.md index 17931ce86..3644ff965 100644 --- a/x/slashing/spec/02_state.md +++ b/x/slashing/spec/02_state.md @@ -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 diff --git a/x/slashing/spec/03_messages.md b/x/slashing/spec/03_messages.md index 6e7d168ff..d680b83cd 100644 --- a/x/slashing/spec/03_messages.md +++ b/x/slashing/spec/03_messages.md @@ -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. diff --git a/x/slashing/spec/05_hooks.md b/x/slashing/spec/05_hooks.md index 8f78cdff0..2e26dfd3d 100644 --- a/x/slashing/spec/05_hooks.md +++ b/x/slashing/spec/05_hooks.md @@ -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 diff --git a/x/slashing/spec/06_events.md b/x/slashing/spec/06_events.md index c5fceda51..cd79a6527 100644 --- a/x/slashing/spec/06_events.md +++ b/x/slashing/spec/06_events.md @@ -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} | diff --git a/x/slashing/spec/07_tombstone.md b/x/slashing/spec/07_tombstone.md index 4759a89b7..216d1836f 100644 --- a/x/slashing/spec/07_tombstone.md +++ b/x/slashing/spec/07_tombstone.md @@ -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 diff --git a/x/slashing/spec/08_params.md b/x/slashing/spec/08_params.md index 0ebfb9e27..defed189a 100644 --- a/x/slashing/spec/08_params.md +++ b/x/slashing/spec/08_params.md @@ -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" |