Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

R4R: Emit Warning Events when Validator Misses Blocks #4629

Merged
merged 12 commits into from
Jun 28, 2019
2 changes: 2 additions & 0 deletions .pending/improvements/sdk/4629-Added-event-that-get
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Added warning event that gets emitted if validator misses certain number of blocks, and re-emits periodically.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Added warning event that gets emitted if validator misses certain number of blocks, and re-emits periodically.
#4629 Added warning event that gets emitted if validator misses certain number of blocks, and re-emits periodically.

Added parameter `DowntimeWarning` to define both the threshold of missed blocks before warning is emitted and also defines granularity of subsequent warnings if validator continues missing blocks.
19 changes: 18 additions & 1 deletion docs/spec/slashing/04_begin_block.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ single slashing period is capped as described in [overview.md](overview.md) unde

## Uptime tracking

At the beginning of each block, we update the signing info for each validator and check if they've dipped below the liveness threshold over the tracked window. If so, they will be slashed by `LivenessSlashAmount` and will be Jailed for `LivenessJailPeriod`. Liveness slashes do NOT lead to a tombstombing.
At the beginning of each block, we update the signing info for each validator and check if they've dipped below the liveness threshold over the tracked window. If so, they will be slashed by `LivenessSlashAmount` and will be Jailed for `LivenessJailPeriod` and an event will be emitted. Liveness slashes do NOT lead to a tombstombing.
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved

If the Validator has missed `DowntimeWarning` blocks in the last signing block window, then a warning event is emitted. The event is re-emitted for every additional `DowntimeWarning` blocks that validator misses in the same window.

Ex: Let `DowntimeWindow = 5`. If a Validator misses 5 blocks, a warning event is emitted. If within the same signing block, he misses an additional 20 blocks, 4 more warning events are emitted with each event tallying the total amount of blocks Validator has missed. If in the next signing block window, Validator doesn't miss a block, no further events will be emitted.

```
height := block.Height
Expand All @@ -108,6 +112,19 @@ for val in block.Validators:
signInfo.MissedBlocksCounter--
// else previous == val not in block.AbsentValidators, no change

// Start emitting events to warn about validator downtime once validator has reached
// downtime warning threshold. Continue emitting warnings every missed `downtimeWarning`
// blocks thereafter
if signInfo.MissedBlocksCounter >= DOWNTIMEWARNING && signInfo.MissedBlocksCounter % DOWNTIMEWARNING == 0 {
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeSlash,
sdk.NewAttribute(types.AttributeKeyAddress, consAddr.String()),
sdk.NewAttribute(types.AttributeKeyMissedBlocks, fmt.Sprintf("%d", signInfo.MissedBlocksCounter)),
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
),
)
}

// 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
Expand Down
5 changes: 5 additions & 0 deletions docs/spec/slashing/06_events.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ The slashing module emits the following events/tags:

- [0] Only included if the validator is jailed.

| Type | Attribute Key | Attribute Value |
|-------|---------------|-----------------------------|
| slash | address | {validatorConsensusAddress} |
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
| slash | missed_blocks | {missedBlocksCounter} |
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved

## Handlers

### MsgUnjail
Expand Down
1 change: 1 addition & 0 deletions docs/spec/slashing/08_params.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ The slashing module contains the following parameters:
| DowntimeJailDuration | string (time ns) | "600000000000" |
| SlashFractionDoubleSign | string (dec) | "0.050000000000000000" |
| SlashFractionDowntime | string (dec) | "0.010000000000000000" |
| DowntimeWarning | string (int64) | "5" |
8 changes: 8 additions & 0 deletions simapp/sim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,14 @@ func genSlashingGenesisState(
})
return v
}(r),
func(r *rand.Rand) int64 {
var v int64
ap.GetOrGenerate(cdc, simulation.DowntimeWarning, &v, r,
func(r *rand.Rand) {
v = simulation.ModuleParamSimulator[simulation.DowntimeWarning](r).(int64)
})
return v
}(r),
),
nil,
nil,
Expand Down
2 changes: 1 addition & 1 deletion types/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,4 +311,4 @@ func (pst *thePast) getOp(ver int64) (Op, bool) {
return Op{}, false
}
return pst.ops[ver-1], true
}
}
4 changes: 4 additions & 0 deletions x/simulation/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const (
DowntimeJailDuration = "downtime_jail_duration"
SlashFractionDoubleSign = "slash_fraction_double_sign"
SlashFractionDowntime = "slash_fraction_downtime"
DowntimeWarning = "downtime_warning"
InflationRateChange = "inflation_rate_change"
Inflation = "inflation"
InflationMax = "inflation_max"
Expand Down Expand Up @@ -121,6 +122,9 @@ var (
SlashFractionDowntime: func(r *rand.Rand) interface{} {
return sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(200) + 1)))
},
DowntimeWarning: func(r *rand.Rand) interface{} {
return int64(RandIntBetween(r, 1, 10))
},
InflationRateChange: func(r *rand.Rand) interface{} {
return sdk.NewDecWithPrec(int64(r.Intn(99)), 2)
},
Expand Down
14 changes: 14 additions & 0 deletions x/slashing/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,20 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
// Array value at this index has not changed, no need to update counter
}

downtimeWarn := k.DowntimeWarning(ctx)
// Start emitting events to warn about validator downtime once validator has reached
// downtime warning threshold. Continue emitting warnings every missed `downtimeWarning`
// blocks thereafter
if signInfo.MissedBlocksCounter >= downtimeWarn && signInfo.MissedBlocksCounter%downtimeWarn == 0 {
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeSlash,
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
sdk.NewAttribute(types.AttributeKeyAddress, consAddr.String()),
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
sdk.NewAttribute(types.AttributeKeyMissedBlocks, fmt.Sprintf("%d", signInfo.MissedBlocksCounter)),
),
)
}

if missed {
logger.Info(fmt.Sprintf("Absent validator %s (%v) at height %d, %d missed, threshold %d", addr, pubkey, height, signInfo.MissedBlocksCounter, k.MinSignedPerWindow(ctx)))
}
Expand Down
6 changes: 6 additions & 0 deletions x/slashing/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ func (k Keeper) DowntimeJailDuration(ctx sdk.Context) (res time.Duration) {
return
}

// Downtime Warning threshold
func (k Keeper) DowntimeWarning(ctx sdk.Context) (res int64) {
k.paramspace.Get(ctx, types.KeyDowntimeWarning, &res)
return
}

// SlashFractionDoubleSign
func (k Keeper) SlashFractionDoubleSign(ctx sdk.Context) (res sdk.Dec) {
k.paramspace.Get(ctx, types.KeySlashFractionDoubleSign, &res)
Expand Down
9 changes: 5 additions & 4 deletions x/slashing/types/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ package types
var (
EventTypeSlash = "slash"

AttributeKeyAddress = "address"
AttributeKeyPower = "power"
AttributeKeyReason = "reason"
AttributeKeyJailed = "jailed"
AttributeKeyAddress = "address"
AttributeKeyPower = "power"
AttributeKeyReason = "reason"
AttributeKeyJailed = "jailed"
AttributeKeyMissedBlocks = "missed_blocks"

AttributeValueDoubleSign = "double_sign"
AttributeValueMissingSignature = "missing_signature"
Expand Down
14 changes: 11 additions & 3 deletions x/slashing/types/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const (
DefaultMaxEvidenceAge time.Duration = 60 * 2 * time.Second
DefaultSignedBlocksWindow int64 = 100
DefaultDowntimeJailDuration time.Duration = 60 * 10 * time.Second
DefaultDowntimeWarning int64 = 1
)

// The Double Sign Jail period ends at Max Time supported by Amino (Dec 31, 9999 - 23:59:59 GMT)
Expand All @@ -32,6 +33,7 @@ var (
KeyDowntimeJailDuration = []byte("DowntimeJailDuration")
KeySlashFractionDoubleSign = []byte("SlashFractionDoubleSign")
KeySlashFractionDowntime = []byte("SlashFractionDowntime")
KeyDowntimeWarning = []byte("DowntimeWarning")
)

// ParamKeyTable for slashing module
Expand All @@ -47,12 +49,14 @@ type Params struct {
DowntimeJailDuration time.Duration `json:"downtime_jail_duration"`
SlashFractionDoubleSign sdk.Dec `json:"slash_fraction_double_sign"`
SlashFractionDowntime sdk.Dec `json:"slash_fraction_downtime"`
DowntimeWarning int64 `json:"downtime_warning"`
}

// NewParams creates a new Params object
func NewParams(maxEvidenceAge time.Duration, signedBlocksWindow int64,
minSignedPerWindow sdk.Dec, downtimeJailDuration time.Duration,
slashFractionDoubleSign sdk.Dec, slashFractionDowntime sdk.Dec) Params {
slashFractionDoubleSign sdk.Dec, slashFractionDowntime sdk.Dec,
downtimeWarning int64) Params {

return Params{
MaxEvidenceAge: maxEvidenceAge,
Expand All @@ -61,6 +65,7 @@ func NewParams(maxEvidenceAge time.Duration, signedBlocksWindow int64,
DowntimeJailDuration: downtimeJailDuration,
SlashFractionDoubleSign: slashFractionDoubleSign,
SlashFractionDowntime: slashFractionDowntime,
DowntimeWarning: downtimeWarning,
}
}

Expand All @@ -71,10 +76,11 @@ func (p Params) String() string {
MinSignedPerWindow: %s
DowntimeJailDuration: %s
SlashFractionDoubleSign: %s
SlashFractionDowntime: %s`, p.MaxEvidenceAge,
SlashFractionDowntime: %s
DowntimeWarning: %d`, p.MaxEvidenceAge,
p.SignedBlocksWindow, p.MinSignedPerWindow,
p.DowntimeJailDuration, p.SlashFractionDoubleSign,
p.SlashFractionDowntime)
p.SlashFractionDowntime, p.DowntimeWarning)
}

// Implements params.ParamSet
Expand All @@ -86,6 +92,7 @@ func (p *Params) ParamSetPairs() params.ParamSetPairs {
{KeyDowntimeJailDuration, &p.DowntimeJailDuration},
{KeySlashFractionDoubleSign, &p.SlashFractionDoubleSign},
{KeySlashFractionDowntime, &p.SlashFractionDowntime},
{KeyDowntimeWarning, &p.DowntimeWarning},
}
}

Expand All @@ -98,5 +105,6 @@ func DefaultParams() Params {
DowntimeJailDuration: DefaultDowntimeJailDuration,
SlashFractionDoubleSign: DefaultSlashFractionDoubleSign,
SlashFractionDowntime: DefaultSlashFractionDowntime,
DowntimeWarning: DefaultDowntimeWarning,
}
}