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:
parent
df95ca8826
commit
d39c42c987
|
@ -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).
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
# Spec Proposals
|
||||
|
||||
- [F1 Fee Distribution](./f1-fee-distribution/f1_fee_distr.pdf)
|
|
@ -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\""];
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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} |
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" |
|
||||
|
|
Loading…
Reference in New Issue