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

fix: introduce VoterSetCounter #347

Merged
merged 7 commits into from
Oct 14, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 20 additions & 44 deletions client/docs/swagger-ui/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14283,16 +14283,6 @@ paths:
properties:
address:
type: string
start_height:
type: string
format: int64
title: >-
height at which validator was first a candidate OR was
unjailed
index_offset:
type: string
format: int64
title: index offset into signed block bit array
jailed_until:
type: string
format: date-time
Expand All @@ -14311,6 +14301,10 @@ paths:
title: >-
missed blocks counter (to avoid scanning the array every
time)
voter_set_counter:
type: string
format: int64
title: how many times the validator joined to voter set
description: >-
ValidatorSigningInfo defines a validator's signing info for
monitoring their
Expand Down Expand Up @@ -14436,16 +14430,6 @@ paths:
properties:
address:
type: string
start_height:
type: string
format: int64
title: >-
height at which validator was first a candidate OR was
unjailed
index_offset:
type: string
format: int64
title: index offset into signed block bit array
jailed_until:
type: string
format: date-time
Expand All @@ -14464,6 +14448,10 @@ paths:
title: >-
missed blocks counter (to avoid scanning the array every
time)
voter_set_counter:
type: string
format: int64
title: how many times the validator joined to voter set
description: >-
ValidatorSigningInfo defines a validator's signing info for
monitoring their
Expand Down Expand Up @@ -42581,14 +42569,6 @@ definitions:
properties:
address:
type: string
start_height:
type: string
format: int64
title: height at which validator was first a candidate OR was unjailed
index_offset:
type: string
format: int64
title: index offset into signed block bit array
jailed_until:
type: string
format: date-time
Expand All @@ -42605,6 +42585,10 @@ definitions:
type: string
format: int64
title: missed blocks counter (to avoid scanning the array every time)
voter_set_counter:
type: string
format: int64
title: how many times the validator joined to voter set
description: >-
ValidatorSigningInfo defines a validator's signing info for monitoring
their
Expand All @@ -42626,14 +42610,6 @@ definitions:
properties:
address:
type: string
start_height:
type: string
format: int64
title: height at which validator was first a candidate OR was unjailed
index_offset:
type: string
format: int64
title: index offset into signed block bit array
jailed_until:
type: string
format: date-time
Expand All @@ -42650,6 +42626,10 @@ definitions:
type: string
format: int64
title: missed blocks counter (to avoid scanning the array every time)
voter_set_counter:
type: string
format: int64
title: how many times the validator joined to voter set
description: >-
ValidatorSigningInfo defines a validator's signing info for
monitoring their
Expand Down Expand Up @@ -42691,14 +42671,6 @@ definitions:
properties:
address:
type: string
start_height:
type: string
format: int64
title: height at which validator was first a candidate OR was unjailed
index_offset:
type: string
format: int64
title: index offset into signed block bit array
jailed_until:
type: string
format: date-time
Expand All @@ -42715,6 +42687,10 @@ definitions:
type: string
format: int64
title: missed blocks counter (to avoid scanning the array every time)
voter_set_counter:
type: string
format: int64
title: how many times the validator joined to voter set
description: >-
ValidatorSigningInfo defines a validator's signing info for monitoring
their
Expand Down
3 changes: 1 addition & 2 deletions docs/core/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -7941,11 +7941,10 @@ liveness activity.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `address` | [string](#string) | | |
| `start_height` | [int64](#int64) | | height at which validator was first a candidate OR was unjailed |
| `index_offset` | [int64](#int64) | | index offset into signed block bit array |
| `jailed_until` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | timestamp validator cannot be unjailed until |
| `tombstoned` | [bool](#bool) | | whether or not a validator has been tombstoned (killed out of validator set) |
| `missed_blocks_counter` | [int64](#int64) | | missed blocks counter (to avoid scanning the array every time) |
| `voter_set_counter` | [int64](#int64) | | how many times the validator joined to voter set |



Expand Down
12 changes: 5 additions & 7 deletions proto/lbm/slashing/v1/slashing.proto
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,16 @@ message ValidatorSigningInfo {
option (gogoproto.goproto_stringer) = false;

string address = 1;
// 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
int64 index_offset = 3 [(gogoproto.moretags) = "yaml:\"index_offset\""];
// timestamp validator cannot be unjailed until
google.protobuf.Timestamp jailed_until = 4
google.protobuf.Timestamp jailed_until = 2
[(gogoproto.moretags) = "yaml:\"jailed_until\"", (gogoproto.stdtime) = true, (gogoproto.nullable) = false];
// whether or not a validator has been tombstoned (killed out of validator
// set)
bool tombstoned = 5;
bool tombstoned = 3;
// missed blocks counter (to avoid scanning the array every time)
int64 missed_blocks_counter = 6 [(gogoproto.moretags) = "yaml:\"missed_blocks_counter\""];
int64 missed_blocks_counter = 4 [(gogoproto.moretags) = "yaml:\"missed_blocks_counter\""];
// how many times the validator joined to voter set
int64 voter_set_counter = 5 [(gogoproto.moretags) = "yaml:\"voter_set_counter\""];
}

// Params represents the parameters used for by the slashing module.
Expand Down
4 changes: 2 additions & 2 deletions simapp/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,11 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []

/* Handle slashing state. */

// reset start height on signing infos
// reset voter set counter on signing infos
app.SlashingKeeper.IterateValidatorSigningInfos(
ctx,
func(addr sdk.ConsAddress, info slashingtypes.ValidatorSigningInfo) (stop bool) {
info.StartHeight = 0
info.VoterSetCounter = 0
app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info)
return false
},
Expand Down
3 changes: 1 addition & 2 deletions x/slashing/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ func TestBeginBlocker(t *testing.T) {

info, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.BytesToConsAddress(pk.Address()))
require.True(t, found)
require.Equal(t, ctx.BlockHeight(), info.StartHeight)
require.Equal(t, int64(1), info.IndexOffset)
require.Equal(t, int64(1), info.VoterSetCounter)
require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil)
require.Equal(t, int64(0), info.MissedBlocksCounter)

Expand Down
7 changes: 3 additions & 4 deletions x/slashing/client/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (s *IntegrationTestSuite) TestGetCmdQuerySigningInfo() {
fmt.Sprintf("--%s=1", flags.FlagHeight),
},
false,
fmt.Sprintf("{\"address\":\"%s\",\"start_height\":\"0\",\"index_offset\":\"0\",\"jailed_until\":\"1970-01-01T00:00:00Z\",\"tombstoned\":false,\"missed_blocks_counter\":\"0\"}", sdk.BytesToConsAddress(val.PubKey.Address())),
fmt.Sprintf("{\"address\":\"%s\",\"jailed_until\":\"1970-01-01T00:00:00Z\",\"tombstoned\":false,\"missed_blocks_counter\":\"0\",\"voter_set_counter\":\"0\"}", sdk.BytesToConsAddress(val.PubKey.Address())),
},
{
"valid address (text output)",
Expand All @@ -79,11 +79,10 @@ func (s *IntegrationTestSuite) TestGetCmdQuerySigningInfo() {
},
false,
fmt.Sprintf(`address: %s
index_offset: "0"
jailed_until: "1970-01-01T00:00:00Z"
missed_blocks_counter: "0"
start_height: "0"
tombstoned: false`, sdk.BytesToConsAddress(val.PubKey.Address())),
tombstoned: false
voter_set_counter: "0"`, sdk.BytesToConsAddress(val.PubKey.Address())),
},
}

Expand Down
8 changes: 4 additions & 4 deletions x/slashing/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ func TestExportAndInitGenesis(t *testing.T) {
app.SlashingKeeper.SetParams(ctx, testslashing.TestParams())

addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.TokensFromConsensusPower(200))
info1 := types.NewValidatorSigningInfo(addrDels[0].ToConsAddress(), int64(4), int64(3),
time.Now().UTC().Add(100000000000), false, int64(10))
info2 := types.NewValidatorSigningInfo(addrDels[1].ToConsAddress(), int64(5), int64(4),
time.Now().UTC().Add(10000000000), false, int64(10))
info1 := types.NewValidatorSigningInfo(addrDels[0].ToConsAddress(),
time.Now().UTC().Add(100000000000), false, int64(10), int64(3))
info2 := types.NewValidatorSigningInfo(addrDels[1].ToConsAddress(),
time.Now().UTC().Add(10000000000), false, int64(10), int64(4))

app.SlashingKeeper.SetValidatorSigningInfo(ctx, addrDels[0].ToConsAddress(), info1)
app.SlashingKeeper.SetValidatorSigningInfo(ctx, addrDels[1].ToConsAddress(), info2)
Expand Down
19 changes: 9 additions & 10 deletions x/slashing/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func TestJailedValidatorDelegations(t *testing.T) {
staking.EndBlocker(ctx, app.StakingKeeper)

// set dummy signing info
newInfo := types.NewValidatorSigningInfo(consAddr, 0, 0, time.Unix(0, 0), false, 0)
newInfo := types.NewValidatorSigningInfo(consAddr, time.Unix(0, 0), false, 0, 0)
app.SlashingKeeper.SetValidatorSigningInfo(ctx, consAddr, newInfo)

// delegate tokens to the validator
Expand Down Expand Up @@ -139,7 +139,7 @@ func TestInvalidMsg(t *testing.T) {
}

// Test a validator through uptime, downtime, revocation,
// unrevocation, starting height reset, and revocation again
// unrevocation, voter set counter reset, and revocation again
func TestHandleAbsentValidator(t *testing.T) {
// initial setup
app := simapp.Setup(false)
Expand All @@ -165,8 +165,7 @@ func TestHandleAbsentValidator(t *testing.T) {
// will exist since the validator has been bonded
info, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.BytesToConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, int64(0), info.IndexOffset)
require.Equal(t, int64(0), info.VoterSetCounter)
require.Equal(t, int64(0), info.MissedBlocksCounter)
require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil)
height := int64(0)
Expand All @@ -178,7 +177,7 @@ func TestHandleAbsentValidator(t *testing.T) {
}
info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.BytesToConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, int64(1000), info.VoterSetCounter)
require.Equal(t, int64(0), info.MissedBlocksCounter)

// 500 blocks missed
Expand All @@ -188,7 +187,7 @@ func TestHandleAbsentValidator(t *testing.T) {
}
info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.BytesToConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, int64(1500), info.VoterSetCounter)
require.Equal(t, app.SlashingKeeper.SignedBlocksWindow(ctx)-app.SlashingKeeper.MinSignedPerWindow(ctx), info.MissedBlocksCounter)

// validator should be bonded still
Expand All @@ -203,7 +202,7 @@ func TestHandleAbsentValidator(t *testing.T) {
app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false)
info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.BytesToConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, int64(1501), info.VoterSetCounter)
// counter now reset to zero
require.Equal(t, int64(0), info.MissedBlocksCounter)

Expand All @@ -225,7 +224,7 @@ func TestHandleAbsentValidator(t *testing.T) {
app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false)
info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.BytesToConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, int64(1502), info.VoterSetCounter)
require.Equal(t, int64(1), info.MissedBlocksCounter)

// end block
Expand Down Expand Up @@ -256,10 +255,10 @@ func TestHandleAbsentValidator(t *testing.T) {
// validator should have been slashed
require.True(t, amt.Sub(slashAmt).Equal(app.BankKeeper.GetBalance(ctx, bondPool.GetAddress(), app.StakingKeeper.BondDenom(ctx)).Amount))

// Validator start height should not have been changed
// Validator voter set counter should not have been changed
info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.BytesToConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, int64(1502), info.VoterSetCounter)
// we've missed 2 blocks more than the maximum, so the counter was reset to 0 at 1 block more and is now 1
require.Equal(t, int64(1), info.MissedBlocksCounter)

Expand Down
8 changes: 4 additions & 4 deletions x/slashing/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ func (suite *SlashingTestSuite) SetupTest() {

addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.TokensFromConsensusPower(200))

info1 := types.NewValidatorSigningInfo(addrDels[0].ToConsAddress(), int64(4), int64(3),
time.Unix(2, 0), false, int64(10))
info2 := types.NewValidatorSigningInfo(addrDels[1].ToConsAddress(), int64(5), int64(4),
time.Unix(2, 0), false, int64(10))
info1 := types.NewValidatorSigningInfo(addrDels[0].ToConsAddress(),
time.Unix(2, 0), false, int64(10), int64(3))
info2 := types.NewValidatorSigningInfo(addrDels[1].ToConsAddress(),
time.Unix(2, 0), false, int64(10), int64(4))

app.SlashingKeeper.SetValidatorSigningInfo(ctx, addrDels[0].ToConsAddress(), info1)
app.SlashingKeeper.SetValidatorSigningInfo(ctx, addrDels[1].ToConsAddress(), info2)
Expand Down
5 changes: 2 additions & 3 deletions x/slashing/keeper/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@ import (
)

func (k Keeper) AfterValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, _ sdk.ValAddress) {
// Update the signing info start height or create a new signing info
// Update the signing info voter set counter or create a new signing info
_, found := k.GetValidatorSigningInfo(ctx, address)
if !found {
signingInfo := types.NewValidatorSigningInfo(
address,
ctx.BlockHeight(),
0,
time.Unix(0, 0),
false,
0,
0,
)
k.SetValidatorSigningInfo(ctx, address, signingInfo)
}
Expand Down
16 changes: 8 additions & 8 deletions x/slashing/keeper/infractions.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr cryptotypes.Addre
}

// this is a relative index, so it counts blocks the validator *should* have signed
// will use the 0-value default signing info if not present, except for start height
index := signInfo.IndexOffset % k.SignedBlocksWindow(ctx)
signInfo.IndexOffset++
// will use the 0-value default signing info if not present, except for the beginning
signInfo.VoterSetCounter++
voterSetCounter := signInfo.VoterSetCounter
index := (voterSetCounter - 1) % k.SignedBlocksWindow(ctx)

// Update signed block bit array & counter
// This counter just tracks the sum of the bit array
Expand Down Expand Up @@ -69,11 +70,11 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr cryptotypes.Addre
)
}

minHeight := signInfo.StartHeight + k.SignedBlocksWindow(ctx)
minVoterSetCount := k.SignedBlocksWindow(ctx)
maxMissed := k.SignedBlocksWindow(ctx) - minSignedPerWindow

// if we are past the minimum height and the validator has missed too many blocks, punish them
if height > minHeight && signInfo.MissedBlocksCounter > maxMissed {
// if we have joined enough times to voter set and the validator has missed too many blocks, punish them
if voterSetCounter > minVoterSetCount && signInfo.MissedBlocksCounter > maxMissed {
validator := k.sk.ValidatorByConsAddr(ctx, consAddr)
if validator != nil && !validator.IsJailed() {
// Downtime confirmed: slash and jail the validator
Expand All @@ -100,14 +101,13 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr cryptotypes.Addre

// We need to reset the counter & array so that the validator won't be immediately slashed for downtime upon rebonding.
signInfo.MissedBlocksCounter = 0
signInfo.IndexOffset = 0
k.clearValidatorMissedBlockBitArray(ctx, consAddr)

logger.Info(
"slashing and jailing validator due to liveness fault",
"height", height,
"validator", consAddr.String(),
"min_height", minHeight,
"voter_set_counter", voterSetCounter,
"threshold", minSignedPerWindow,
"slashed", k.SlashFractionDowntime(ctx).String(),
"jailed_until", signInfo.JailedUntil,
Expand Down
Loading