Skip to content

Commit

Permalink
Merge pull request #1263 from cosmos/bucky/staking-spec-updates
Browse files Browse the repository at this point in the history
spec updates
  • Loading branch information
ebuchman authored Jun 16, 2018
2 parents 1f88b0b + 190d87d commit d083287
Show file tree
Hide file tree
Showing 14 changed files with 269 additions and 176 deletions.
17 changes: 8 additions & 9 deletions docs/spec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ the Cosmos Hub.

NOTE: the specifications are not yet complete and very much a work in progress.

- [Basecoin](basecoin) - Cosmos SDK related specifications and transactions for
sending tokens.
- [Governance](governance) - Governance related specifications including
proposals and voting.
- [IBC](ibc) - Specification of the Cosmos inter-blockchain communication (IBC) protocol.
- [Staking](staking) - Proof-of-stake related specifications including bonding
and delegation transactions, inflation, etc.
- [Slashing](slashing) - Specifications of validator punishment mechanisms
- [Provisioning](provisioning) - Fee distribution, and atom provision distribution specification
- [Store](store) - The core Merkle store that holds the state.
- [Auth](auth) - The structure and authnentication of accounts and transactions.
- [Bank](bank) - Sending tokens.
- [Governance](governance) - Proposals and voting.
- [IBC](ibc) - Inter-Blockchain Communication (IBC) protocol.
- [Staking](staking) - Proof-of-stake bonding, delegation, etc.
- [Slashing](slashing) - Validator punishment mechanisms.
- [Provisioning](provisioning) - Fee distribution, and atom provision distribution
- [Other](other) - Other components of the Cosmos Hub, including the reserve
pool, All in Bits vesting, etc.

Expand Down
100 changes: 0 additions & 100 deletions docs/spec/WIP_slashing/valset-changes.md

This file was deleted.

Empty file added docs/spec/auth/state.md
Empty file.
Empty file added docs/spec/auth/transactions.md
Empty file.
File renamed without changes.
Empty file added docs/spec/bank/transactions.md
Empty file.
File renamed without changes.
File renamed without changes.
102 changes: 102 additions & 0 deletions docs/spec/slashing/end_block.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# End-Block

## Slashing

Tendermint blocks can include
[Evidence](https://github.com/tendermint/tendermint/blob/develop/docs/spec/blockchain/blockchain.md#evidence), which indicates that a validator
committed malicious behaviour. The relevant information is forwarded to the
application as [ABCI
Evidence](https://github.com/tendermint/abci/blob/develop/types/types.proto#L259), so the validator an be accordingly punished.

For some `evidence` to be valid, it must satisfy:

`evidence.Timestamp >= block.Timestamp - MAX_EVIDENCE_AGE`

where `evidence.Timestamp` is the timestamp in the block at height
`evidence.Height` and `block.Timestamp` is the current block timestamp.

If valid evidence is included in a block, the validator's stake is reduced by `SLASH_PROPORTION` of
what their stake was when the equivocation occurred (rather than when the evidence was discovered):

```
curVal := validator
oldVal := loadValidator(evidence.Height, evidence.Address)
slashAmount := SLASH_PROPORTION * oldVal.Shares
curVal.Shares = max(0, curVal.Shares - slashAmount)
```

This ensures that offending validators are punished the same amount whether they
act as a single validator with X stake or as N validators with collectively X
stake.

We also need to loop through the unbondings and redelegations to slash them as
well:

```
unbondings := getUnbondings(validator.Address)
for unbond in unbondings {
if was not bonded before evidence.Height {
continue
}
unbond.InitialTokens
burn := unbond.InitialTokens * SLASH_PROPORTION
unbond.Tokens = max(0, unbond.Tokens - burn)
}
// only care if source gets slashed because we're already bonded to destination
// so if destination validator gets slashed our delegation just has same shares
// of smaller pool.
redels := getRedelegationsBySource(validator.Address)
for redel in redels {
if was not bonded before evidence.Height {
continue
}
burn := redel.InitialTokens * SLASH_PROPORTION
amount := unbondFromValidator(redel.Destination, burn)
destroy(amount)
}
```

## Automatic Unbonding

At the beginning of each block, we update the signing info for each validator and check if they should be automatically unbonded:

```
height := block.Height
for val in block.Validators:
signInfo = SigningInfo.Get(val.Address)
if signInfo == nil{
signInfo.StartHeight = height
}
index := signInfo.IndexOffset % SIGNED_BLOCKS_WINDOW
signInfo.IndexOffset++
previous = SigningBitArray.Get(val.Address, index)
// update counter if array has changed
if previous and val in block.AbsentValidators:
SigningBitArray.Set(val.Address, index, false)
signInfo.SignedBlocksCounter--
else if !previous and val not in block.AbsentValidators:
SigningBitArray.Set(val.Address, index, true)
signInfo.SignedBlocksCounter++
// else previous == val not in block.AbsentValidators, no change
// validator must be active for at least SIGNED_BLOCKS_WINDOW
// before they can be automatically unbonded for failing to be
// included in 50% of the recent LastCommits
minHeight = signInfo.StartHeight + SIGNED_BLOCKS_WINDOW
minSigned = SIGNED_BLOCKS_WINDOW / 2
if height > minHeight AND signInfo.SignedBlocksCounter < minSigned:
signInfo.JailedUntil = block.Time + DOWNTIME_UNBOND_DURATION
slash & unbond the validator
SigningInfo.Set(val.Address, signInfo)
```
51 changes: 51 additions & 0 deletions docs/spec/slashing/state.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
## State

### Signing Info

Every block includes a set of precommits by the validators for the previous block,
known as the LastCommit. A LastCommit is valid so long as it contains precommits from +2/3 of voting power.

Proposers are incentivized to include precommits from all
validators in the LastCommit by receiving additional fees
proportional to the difference between the voting power included in the
LastCommit and +2/3 (see [TODO](https://github.com/cosmos/cosmos-sdk/issues/967)).

Validators are penalized for failing to be included in the LastCommit for some
number of blocks by being automatically unbonded.

Information about validator activity is tracked in a `ValidatorSigningInfo`.
It is indexed in the store as follows:

- SigningInfo: ` 0x01 | ValTendermintAddr -> amino(valSigningInfo)`
- SigningBitArray: ` 0x02 | ValTendermintAddr | LittleEndianUint64(signArrayIndex) -> VarInt(didSign)`

The first map allows us to easily lookup the recent signing info for a
validator, according to the Tendermint validator address. The second map acts as
a bit-array of size `SIGNED_BLOCKS_WINDOW` that tells us if the validator signed 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 sign the corresponding block, and `1` indicates they did.

Note that the SigningBitArray is not explicitly initialized up-front. Keys are
added as we progress through the first `SIGNED_BLOCKS_WINDOW` blocks for a newly
bonded validator.

The information stored for tracking validator liveness is as follows:

```go
type ValidatorSigningInfo struct {
StartHeight int64
IndexOffset int64
JailedUntil int64
SignedBlocksCounter int64
}

```

Where:
* `StartHeight` is set to the height that the candidate became an active validator (with non-zero voting power).
* `IndexOffset` is incremented each time the candidate was a bonded validator in a block (and may have signed a precommit or not).
* `JailedUntil` is set whenever the candidate is revoked due to downtime
* `SignedBlocksCounter` is a counter kept to avoid unnecessary array reads. `SignedBlocksBitArray.Sum() == SignedBlocksCounter` always.
File renamed without changes.
Loading

0 comments on commit d083287

Please sign in to comment.