diff --git a/docs/spec/slashing/README.md b/docs/spec/slashing/README.md
index 7e8b780a2d..d50d95d36e 100644
--- a/docs/spec/slashing/README.md
+++ b/docs/spec/slashing/README.md
@@ -6,8 +6,11 @@ This section specifies the slashing module of the Cosmos SDK, which implements f
first outlined in the [Cosmos Whitepaper](https://cosmos.network/about/whitepaper) in June 2016.
The slashing module enables Cosmos SDK-based blockchains to disincentivize any attributable action
-by a protocol-recognized actor with value at stake by "slashing" them: burning some amount of their
-stake - and possibly also removing their ability to vote on future blocks for a period of time.
+by a protocol-recognized actor with value at stake by penalizing them ("slashing").
+
+Penalties may include, but are not limited to:
+- Burning some amount of their stake
+- Removing their ability to vote on future blocks for a period of time.
This module will be used by the Cosmos Hub, the first hub in the Cosmos ecosystem.
@@ -16,14 +19,15 @@ This module will be used by the Cosmos Hub, the first hub in the Cosmos ecosyste
1. **[State](state.md)**
1. [SigningInfo](state.md#signing-info)
1. [SlashingPeriod](state.md#slashing-period)
-1. **[State Machine](state-machine.md)**
- 1. [Transactions](state-machine.md#transactions)
- 1. Unjail
- 1. [Interactions](state-machine.md#interactions)
- 1. Validator Bonded
- 1. Validator Unbonding
- 1. Validator Slashed
- 1. [State Cleanup](state-machine.md#state-cleanup)
+1. **[Overview](overview.md)**
+1. **[Transactions](transactions.md)**
+ 1. Unjail
+1. **[Hooks](hooks.md)**
+ 1. Validator Bonded
+ 1. Validator Unbonding
+ 1. Validator Slashed
1. **[Begin Block](begin-block.md)**
1. [Evidence handling](begin-block.md#evidence-handling)
1. [Uptime tracking](begin-block.md#uptime-tracking)
+1. **[Future Improvements](future-improvements.md)**
+ 1. [State cleanup](future-improvements.md#state-cleanup)
diff --git a/docs/spec/slashing/begin-block.md b/docs/spec/slashing/begin-block.md
index 0ab8883179..375e191859 100644
--- a/docs/spec/slashing/begin-block.md
+++ b/docs/spec/slashing/begin-block.md
@@ -76,7 +76,7 @@ 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.
-Double signature slashes are capped by the slashing period as described in [state-machine.md](state-machine.md).
+The amount slashed for all double signature infractions committed within a single slashing period is capped as described in [state-machine.md](state-machine.md).
## Uptime tracking
@@ -117,4 +117,4 @@ for val in block.Validators:
SigningInfo.Set(val.Address, signInfo)
```
-Downtime slashes are *not* capped by the slashing period, although they do reset it (since the validator is unbonded).
+The amount slashed for downtime slashes is *not* capped by the slashing period in which they are committed, although they do reset it (since the validator is unbonded).
diff --git a/docs/spec/slashing/future-improvements.md b/docs/spec/slashing/future-improvements.md
new file mode 100644
index 0000000000..84be139e8c
--- /dev/null
+++ b/docs/spec/slashing/future-improvements.md
@@ -0,0 +1,4 @@
+## State Cleanup
+
+Once no evidence for a given slashing period can possibly be valid (the end time plus the unbonding period is less than the current time),
+old slashing periods should be cleaned up. This will be implemented post-launch.
diff --git a/docs/spec/slashing/hooks.md b/docs/spec/slashing/hooks.md
new file mode 100644
index 0000000000..36dde61f94
--- /dev/null
+++ b/docs/spec/slashing/hooks.md
@@ -0,0 +1,58 @@
+## Hooks
+
+In this section we describe the "hooks" - slashing module code that runs when other events happen.
+
+### Validator Bonded
+
+Upon successful bonding of a validator (a given validator entering the "bonded" state,
+which may happen on delegation, on unjailing, etc), we create a new `SlashingPeriod` structure for the
+now-bonded validator, which `StartHeight` of the current block, `EndHeight` of `0` (sentinel value for not-yet-ended),
+and `SlashedSoFar` of `0`:
+
+```
+onValidatorBonded(address sdk.ValAddress)
+
+ slashingPeriod = SlashingPeriod{
+ ValidatorAddr : address,
+ StartHeight : CurrentHeight,
+ EndHeight : 0,
+ SlashedSoFar : 0,
+ }
+ setSlashingPeriod(slashingPeriod)
+
+ return
+```
+
+### Validator Unbonded
+
+When a validator is unbonded, we update the in-progress `SlashingPeriod` with the current block as the `EndHeight`:
+
+```
+onValidatorUnbonded(address sdk.ValAddress)
+
+ slashingPeriod = getSlashingPeriod(address, CurrentHeight)
+ slashingPeriod.EndHeight = CurrentHeight
+ setSlashingPeriod(slashingPeriod)
+
+ return
+```
+
+### Validator Slashed
+
+When a validator is slashed, we look up the appropriate `SlashingPeriod` based on the validator
+address and the time of infraction, cap the fraction slashed as `max(SlashFraction, SlashedSoFar)`
+(which may be `0`), and update the `SlashingPeriod` with the increased `SlashedSoFar`:
+
+```
+beforeValidatorSlashed(address sdk.ValAddress, fraction sdk.Rat, infractionHeight int64)
+
+ slashingPeriod = getSlashingPeriod(address, infractionHeight)
+ totalToSlash = max(slashingPeriod.SlashedSoFar, fraction)
+ slashingPeriod.SlashedSoFar = totalToSlash
+ setSlashingPeriod(slashingPeriod)
+
+ remainderToSlash = slashingPeriod.SlashedSoFar - totalToSlash
+ fraction = remainderToSlash
+
+ continue with slashing
+```
diff --git a/docs/spec/slashing/overview.md b/docs/spec/slashing/overview.md
new file mode 100644
index 0000000000..d9a8f39c15
--- /dev/null
+++ b/docs/spec/slashing/overview.md
@@ -0,0 +1,68 @@
+## Conceptual overview
+
+### States
+
+At any given time, there are any number of validators registered in the state machine.
+Each block, the top `n = MaximumBondedValidators` validators who are not jailed become *bonded*, meaning that they may propose and vote on blocks.
+Validators who are *bonded* are *at stake*, meaning that part or all of their stake and their delegators' stake is at risk if they commit a protocol fault.
+
+### Slashing period
+
+In order to mitigate the impact of initially likely categories of non-malicious protocol faults, the Cosmos Hub implements for each validator
+a *slashing period*, in which the amount by which a validator can be slashed is capped at the punishment for the worst violation. For example,
+if you misconfigure your HSM and double-sign a bunch of old blocks, you'll only be punished for the first double-sign (and then immediately jailed,
+so that you have a chance to reconfigure your setup). This will still be quite expensive and desirable to avoid, but slashing periods somewhat blunt
+the economic impact of unintentional misconfiguration.
+
+Unlike the unbonding period, the slashing period doesn't have a fixed length. A new slashing period starts whenever a validator is bonded and ends
+whenever the validator is unbonded (which will happen if the validator is jailed). The amount of tokens slashed relative to validator power for infractions
+committed within the slashing period, whenever they are discovered, is capped at the punishment for the worst infraction
+(which for the Cosmos Hub at launch will be double-signing a block).
+
+#### ASCII timelines
+
+*Code*
+
+*[* : timeline start
+*]* : timeline end
+*<* : slashing period start
+*>* : slashing period end
+*Cn* : infraction `n` committed
+*Dn* : infraction `n` discovered
+*Vb* : validator bonded
+*Vu* : validator unbonded
+
+*Single infraction*
+
+<----------------->
+[----------C1----D1,Vu-----]
+
+A single infraction is committed then later discovered, at which point the validator is unbonded and slashed at the full amount for the infraction.
+
+*Multiple infractions*
+
+<---------------------------->
+[----------C1--C2---C3---D1,D2,D3Vu-----]
+
+Multiple infractions are committed within a single slashing period then later discovered, at which point the validator is unbonded and slashed for only the worst infraction.
+
+*Multiple infractions after rebonding*
+
+
+<----------------------------> <-------------->
+[----------C1--C2---C3---D1,D2,D3Vu---Vb---C4----D4,Vu--]
+
+Multiple infractions are committed within a single slashing period then later discovered, at which point the validator is unbonded and slashed for only the worst infraction.
+The validator then unjails themself and rebonds, then commits a fourth infraction - which is discovered and punished at the full amount, since a new slashing period started
+when they unjailed and rebonded.
+
+### Safety note
+
+Slashing is capped fractionally per period, but the amount of total bonded stake associated with any given validator can change (by an unbounded amount) over that period.
+
+For example, with MaxFractionSlashedPerPeriod = `0.5`, if a validator is initially slashed at `0.4` near the start of a period when they have 100 stake bonded,
+then later slashed at `0.4` when they have `1000` stake bonded, the total amount slashed is just `40 + 100 = 140` (since the latter slash is capped at `0.1`) -
+whereas if they had `1000` stake bonded initially, the first offense would have been slashed for `400` stake and the total amount slashed would have been `400 + 100 = 500`.
+
+This means that any slashing events which utilize the slashing period (are capped-per-period) **must also** jail the validator when the infraction is discovered.
+Otherwise it would be possible for a validator to slash themselves intentionally at a low bond, then increase their bond but no longer be at stake since they would have already hit the `SlashedSoFar` cap.
diff --git a/docs/spec/slashing/state-machine.md b/docs/spec/slashing/state-machine.md
deleted file mode 100644
index e35b6b1794..0000000000
--- a/docs/spec/slashing/state-machine.md
+++ /dev/null
@@ -1,174 +0,0 @@
-# State Machine Interaction Overview
-
-## Conceptual overview
-
-### States
-
-At any given time, there are any number of validator candidates registered in the state machine.
-Each block, the top `n` candidates who are not jailed become *bonded*, meaning that they may propose and vote on blocks.
-Validators who are *bonded* are *at stake*, meaning that part or all of their stake is at risk if they commit a protocol fault.
-
-### Slashing period
-
-In order to mitigate the impact of initially likely categories of non-malicious protocol faults, the Cosmos Hub implements for each validator
-a *slashing period*, in which the amount by which a validator can be slashed is capped at the punishment for the worst violation. For example,
-if you misconfigure your HSM and double-sign a bunch of old blocks, you'll only be punished for the first double-sign (and then immediately jailed,
-so that you have a chance to reconfigure your setup). This will still be quite expensive and desirable to avoid, but slashing periods somewhat blunt
-the economic impact of unintentional misconfiguration.
-
-A new slashing period starts whenever a validator is bonded and ends whenever the validator is unbonded (which will happen if the validator is jailed).
-The amount of tokens slashed relative to validator power for infractions committed within the slashing period, whenever they are discovered, is capped
-at the punishment for the worst infraction (which for the Cosmos Hub at launch will be double-signing a block).
-
-#### ASCII timelines
-
-*Code*
-
-*[* : timeline start
-*]* : timeline end
-*<* : slashing period start
-*>* : slashing period end
-*Cn* : infraction `n` committed
-*Dn* : infraction `n` discovered
-*Vb* : validator bonded
-*Vu* : validator unbonded
-
-*Single infraction*
-
-<----------------->
-[----------C1----D1,Vu-----]
-
-A single infraction is committed then later discovered, at which point the validator is unbonded and slashed at the full amount for the infraction.
-
-*Multiple infractions*
-
-<---------------------------->
-[----------C1--C2---C3---D1,D2,D3Vu-----]
-
-Multiple infractions are committed within a single slashing period then later discovered, at which point the validator is unbonded and slashed for only the worst infraction.
-
-*Multiple infractions after rebonding*
-
-
-<----------------------------> <-------------->
-[----------C1--C2---C3---D1,D2,D3Vu---Vb---C4----D4,Vu--]
-
-Multiple infractions are committed within a single slashing period then later discovered, at which point the validator is unbonded and slashed for only the worst infraction.
-The validator then unjails themself and rebonds, then commits a fourth infraction - which is discovered and punished at the full amount, since a new slashing period started
-when they unjailed and rebonded.
-
-## Transactions
-
-In this section we describe the processing of transactions for the `slashing` module.
-
-### TxUnjail
-
-If a validator was automatically unbonded due to downtime and wishes to come back online &
-possibly rejoin the bonded set, it must send `TxUnjail`:
-
-```
-type TxUnjail struct {
- ValidatorAddr sdk.AccAddress
-}
-
-handleMsgUnjail(tx TxUnjail)
-
- validator = getValidator(tx.ValidatorAddr)
- if validator is nil
- fail with "No validator found"
-
- if validator is not jailed
- fail with "Validator not jailed, cannot unjail"
-
- info = getValidatorSigningInfo(operator)
- if block time is before info.JailedUntil
- fail with "Validator still jailed, cannot unjail until period has expired"
-
- // Update the start height so the validator won't be immediately unbonded again
- info.StartHeight = BlockHeight
- setValidatorSigningInfo(info)
-
- validator.Jailed = false
- setValidator(validator)
-
- return
-```
-
-If the validator has enough stake to be in the top hundred, they will be automatically rebonded,
-and all delegators still delegated to the validator will be rebonded and begin to again collect
-provisions and rewards.
-
-## Interactions
-
-In this section we describe the "hooks" - slashing module code that runs when other events happen.
-
-### Validator Bonded
-
-Upon successful bonding of a validator (a given validator changing from "unbonded" state to "bonded" state,
-which may happen on delegation, on unjailing, etc), we create a new `SlashingPeriod` structure for the
-now-bonded validator, which `StartHeight` of the current block, `EndHeight` of `0` (sentinel value for not-yet-ended),
-and `SlashedSoFar` of `0`:
-
-```
-onValidatorBonded(address sdk.ValAddress)
-
- slashingPeriod = SlashingPeriod{
- ValidatorAddr : address,
- StartHeight : CurrentHeight,
- EndHeight : 0,
- SlashedSoFar : 0,
- }
- setSlashingPeriod(slashingPeriod)
-
- return
-```
-
-### Validator Unbonded
-
-When a validator is unbonded, we update the in-progress `SlashingPeriod` with the current block as the `EndHeight`:
-
-```
-onValidatorUnbonded(address sdk.ValAddress)
-
- slashingPeriod = getSlashingPeriod(address, CurrentHeight)
- slashingPeriod.EndHeight = CurrentHeight
- setSlashingPeriod(slashingPeriod)
-
- return
-```
-
-### Validator Slashed
-
-When a validator is slashed, we look up the appropriate `SlashingPeriod` based on the validator
-address and the time of infraction, cap the fraction slashed as `max(SlashFraction, SlashedSoFar)`
-(which may be `0`), and update the `SlashingPeriod` with the increased `SlashedSoFar`:
-
-```
-beforeValidatorSlashed(address sdk.ValAddress, fraction sdk.Rat, infractionHeight int64)
-
- slashingPeriod = getSlashingPeriod(address, infractionHeight)
- totalToSlash = max(slashingPeriod.SlashedSoFar, fraction)
- slashingPeriod.SlashedSoFar = totalToSlash
- setSlashingPeriod(slashingPeriod)
-
- remainderToSlash = slashingPeriod.SlashedSoFar - totalToSlash
- fraction = remainderToSlash
-
- continue with slashing
-```
-
-##### Safety note
-
-Slashing is capped fractionally per period, but the amount of total bonded stake associated with any given validator can change (by an unbounded amount) over that period.
-
-For example, with MaxFractionSlashedPerPeriod = `0.5`, if a validator is initially slashed at `0.4` near the start of a period when they have 100 stake bonded,
-then later slashed at `0.4` when they have `1000` stake bonded, the total amount slashed is just `40 + 100 = 140` (since the latter slash is capped at `0.1`) -
-whereas if they had `1000` stake bonded initially, the first offense would have been slashed for `400` stake and the total amount slashed would have been `400 + 100 = 500`.
-
-This means that any slashing events which utilize the slashing period (are capped-per-period) **must** *also* jail the validator when the infraction is discovered.
-Otherwise it would be possible for a validator to slash themselves intentionally at a low bond, then increase their bond but no longer be at stake since they would have already hit the `SlashedSoFar` cap.
-
-## State Cleanup
-
-Once no evidence for a given slashing period can possibly be valid (the end time plus the unbonding period is less than the current time),
-old slashing periods should be cleaned up. This will be implemented post-launch.
diff --git a/docs/spec/slashing/state.md b/docs/spec/slashing/state.md
index b64e990403..ae426db7b3 100644
--- a/docs/spec/slashing/state.md
+++ b/docs/spec/slashing/state.md
@@ -54,9 +54,9 @@ Where:
## Slashing Period
A slashing period is a start and end block height associated with a particular validator,
-within which only the "worst infraction counts": the total amount of slashing for
-infractions committed within the period (and discovered whenever) is capped at the
-penalty for the worst offense.
+within which only the "worst infraction counts" (see the [Overview](overview.md)): the total
+amount of slashing for infractions committed within the period (and discovered whenever) is
+capped at the penalty for the worst offense.
This period starts when a validator is first bonded and ends when a validator is slashed & jailed
for any reason. When the validator rejoins the validator set (perhaps through unjailing themselves,
diff --git a/docs/spec/slashing/transactions.md b/docs/spec/slashing/transactions.md
new file mode 100644
index 0000000000..b1d523139f
--- /dev/null
+++ b/docs/spec/slashing/transactions.md
@@ -0,0 +1,40 @@
+## Transactions
+
+In this section we describe the processing of transactions for the `slashing` module.
+
+### TxUnjail
+
+If a validator was automatically unbonded due to downtime and wishes to come back online &
+possibly rejoin the bonded set, it must send `TxUnjail`:
+
+```
+type TxUnjail struct {
+ ValidatorAddr sdk.AccAddress
+}
+
+handleMsgUnjail(tx TxUnjail)
+
+ validator = getValidator(tx.ValidatorAddr)
+ if validator == nil
+ fail with "No validator found"
+
+ if !validator.Jailed
+ fail with "Validator not jailed, cannot unjail"
+
+ info = getValidatorSigningInfo(operator)
+ if block time < info.JailedUntil
+ fail with "Validator still jailed, cannot unjail until period has expired"
+
+ // Update the start height so the validator won't be immediately unbonded again
+ info.StartHeight = BlockHeight
+ setValidatorSigningInfo(info)
+
+ validator.Jailed = false
+ setValidator(validator)
+
+ return
+```
+
+If the validator has enough stake to be in the top `n = MaximumBondedValidators`, they will be automatically rebonded,
+and all delegators still delegated to the validator will be rebonded and begin to again collect
+provisions and rewards.