Skip to content

Commit

Permalink
refactor!: x/slashing missed block window (#15580)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexanderbez committed Apr 10, 2023
1 parent 96662df commit 4684c85
Show file tree
Hide file tree
Showing 24 changed files with 599 additions and 164 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### Improvements

* (x/slashing) [#15580](https://github.com/cosmos/cosmos-sdk/pull/15580) Refactor the validator's missed block signing window to be a chunked bitmap instead of a "logical" bitmap, significantly reducing the storage footprint.
* [#15448](https://github.com/cosmos/cosmos-sdk/pull/15448) Automatically populate the block timestamp for historical queries. In contexts where the block timestamp is needed for previous states, the timestamp will now be set. Note, when querying against a node it must be re-synced in order to be able to automatically populate the block timestamp. Otherwise, the block timestamp will be populated for heights going forward once upgraded.
* (x/gov) [#15554](https://github.com/cosmos/cosmos-sdk/pull/15554) Add proposal result log in `active_proposal` event. When a proposal passes but fails to execute, the proposal result is logged in the `active_proposal` event.
* (mempool) [#15328](https://github.com/cosmos/cosmos-sdk/pull/15328) Improve the `PriorityNonceMempool`
Expand Down Expand Up @@ -98,6 +99,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### State Machine Breaking

* (x/slashing) [#15580](https://github.com/cosmos/cosmos-sdk/pull/15580) The validator slashing window now stores "chunked" bitmap entries for each validator's signing window instead of a single boolean entry per signing window index.
* (x/feegrant) [#14294](https://github.com/cosmos/cosmos-sdk/pull/14294) Moved the logic of rejecting duplicate grant from `msg_server` to `keeper` method.
* (x/staking) [#14590](https://github.com/cosmos/cosmos-sdk/pull/14590) `MsgUndelegateResponse` now includes undelegated amount. `x/staking` module's `keeper.Undelegate` now returns 3 values (completionTime,undelegateAmount,error) instead of 2.

Expand Down
17 changes: 9 additions & 8 deletions api/cosmos/slashing/v1beta1/slashing.pulsar.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/99designs/keyring v1.2.1
github.com/armon/go-metrics v0.4.1
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816
github.com/bits-and-blooms/bitset v1.5.0
github.com/chzyer/readline v1.5.1
github.com/cockroachdb/apd/v2 v2.0.2
github.com/cockroachdb/errors v1.9.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s=
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8=
github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U=
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ=
Expand Down
31 changes: 19 additions & 12 deletions proto/cosmos/slashing/v1beta1/slashing.proto
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,24 @@ message ValidatorSigningInfo {
option (gogoproto.equal) = true;

string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// Height at which validator was first a candidate OR was unjailed
// Height at which validator was first a candidate OR was un-jailed
int64 start_height = 2;
// 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`.
// Index which is incremented every time a validator is bonded in a block and
// _may_ have signed a pre-commit or not. This in conjunction with the
// signed_blocks_window param determines the index in the missed block bitmap.
int64 index_offset = 3;
// Timestamp until which the validator is jailed due to liveness downtime.
google.protobuf.Timestamp jailed_until = 4
[(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (amino.dont_omitempty) = true];
// 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.
google.protobuf.Timestamp jailed_until = 4 [
(gogoproto.stdtime) = true,
(gogoproto.nullable) = false,
(amino.dont_omitempty) = true
];
// 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 misbehavior.
bool tombstoned = 5;
// A counter kept to avoid unnecessary array reads.
// Note that `Sum(MissedBlocksBitArray)` always equals `MissedBlocksCounter`.
// A counter of missed (unsigned) blocks. It is used to avoid unnecessary
// reads in the missed block bitmap.
int64 missed_blocks_counter = 6;
}

Expand All @@ -44,8 +48,11 @@ message Params {
(amino.encoding) = "cosmos_dec_bytes",
(amino.dont_omitempty) = true
];
google.protobuf.Duration downtime_jail_duration = 3
[(gogoproto.nullable) = false, (amino.dont_omitempty) = true, (gogoproto.stdduration) = true];
google.protobuf.Duration downtime_jail_duration = 3 [
(gogoproto.nullable) = false,
(amino.dont_omitempty) = true,
(gogoproto.stdduration) = true
];
bytes slash_fraction_double_sign = 4 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false,
Expand Down
1 change: 1 addition & 0 deletions simapp/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect
github.com/bits-and-blooms/bitset v1.5.0 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions simapp/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s=
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8=
github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U=
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ=
Expand Down
1 change: 1 addition & 0 deletions tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect
github.com/bits-and-blooms/bitset v1.5.0 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions tests/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s=
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8=
github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U=
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ=
Expand Down
10 changes: 7 additions & 3 deletions x/slashing/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import (
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)

// InitGenesis initialize default parameters
// and the keeper's address to pubkey map
// InitGenesis initializes default parameters and the keeper's address to
// pubkey map.
func (keeper Keeper) InitGenesis(ctx sdk.Context, stakingKeeper types.StakingKeeper, data *types.GenesisState) {
stakingKeeper.IterateValidators(ctx,
func(index int64, validator stakingtypes.ValidatorI) bool {
consPk, err := validator.ConsPubKey()
if err != nil {
panic(err)
}

keeper.AddPubkey(ctx, consPk)
return false
},
Expand All @@ -33,8 +34,11 @@ func (keeper Keeper) InitGenesis(ctx sdk.Context, stakingKeeper types.StakingKee
if err != nil {
panic(err)
}

for _, missed := range array.MissedBlocks {
keeper.SetValidatorMissedBlockBitArray(ctx, address, missed.Index, missed.Missed)
if err := keeper.SetMissedBlockBitmapValue(ctx, address, missed.Index, missed.Missed); err != nil {
panic(err)
}
}
}

Expand Down
44 changes: 31 additions & 13 deletions x/slashing/keeper/infractions.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package keeper
import (
"fmt"

"github.com/cockroachdb/errors"

cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
Expand Down Expand Up @@ -31,27 +33,42 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr cryptotypes.Addre
panic(fmt.Sprintf("Expected signing info for validator %s but not found", consAddr))
}

// 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
// Compute the relative index, so we count the blocks the validator *should*
// have signed. We will use the 0-value default signing info if not present,
// except for start height. The index is in the range [0, SignedBlocksWindow)
// and is used to see if a validator signed a block at the given height, which
// is represented by a bit in the bitmap.
index := signInfo.IndexOffset % k.SignedBlocksWindow(ctx)
signInfo.IndexOffset++

// Update signed block bit array & counter
// This counter just tracks the sum of the bit array
// That way we avoid needing to read/write the whole array each time
previous := k.GetValidatorMissedBlockBitArray(ctx, consAddr, index)
// determine if the validator signed the previous block
previous, err := k.GetMissedBlockBitmapValue(ctx, consAddr, index)
if err != nil {
panic(errors.Wrap(err, "failed to get the validator's bitmap value"))
}

missed := !signed
switch {
case !previous && missed:
// Array value has changed from not missed to missed, increment counter
k.SetValidatorMissedBlockBitArray(ctx, consAddr, index, true)
// Bitmap value has changed from not missed to missed, so we flip the bit
// and increment the counter.
if err := k.SetMissedBlockBitmapValue(ctx, consAddr, index, true); err != nil {
panic(err)
}

signInfo.MissedBlocksCounter++

case previous && !missed:
// Array value has changed from missed to not missed, decrement counter
k.SetValidatorMissedBlockBitArray(ctx, consAddr, index, false)
// Bitmap value has changed from missed to not missed, so we flip the bit
// and decrement the counter.
if err := k.SetMissedBlockBitmapValue(ctx, consAddr, index, false); err != nil {
panic(err)
}

signInfo.MissedBlocksCounter--

default:
// Array value at this index has not changed, no need to update counter
// bitmap value at this index has not changed, no need to update counter
}

minSignedPerWindow := k.MinSignedPerWindow(ctx)
Expand Down Expand Up @@ -105,10 +122,11 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr cryptotypes.Addre

signInfo.JailedUntil = ctx.BlockHeader().Time.Add(k.DowntimeJailDuration(ctx))

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

logger.Info(
"slashing and jailing validator due to liveness fault",
Expand Down
8 changes: 8 additions & 0 deletions x/slashing/keeper/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/slashing/exported"
v2 "github.com/cosmos/cosmos-sdk/x/slashing/migrations/v2"
v3 "github.com/cosmos/cosmos-sdk/x/slashing/migrations/v3"
v4 "github.com/cosmos/cosmos-sdk/x/slashing/migrations/v4"
)

// Migrator is a struct for handling in-place store migrations.
Expand All @@ -30,3 +31,10 @@ func (m Migrator) Migrate1to2(ctx sdk.Context) error {
func (m Migrator) Migrate2to3(ctx sdk.Context) error {
return v3.Migrate(ctx, ctx.KVStore(m.keeper.storeKey), m.legacySubspace, m.keeper.cdc)
}

// Migrate3to4 migrates the x/slashing module state from the consensus
// version 3 to version 4. Specifically, it migrates the validator missed block
// bitmap.
func (m Migrator) Migrate3to4(ctx sdk.Context) error {
return v4.Migrate(ctx, m.keeper.cdc, ctx.KVStore(m.keeper.storeKey), m.keeper.GetParams(ctx))
}
Loading

0 comments on commit 4684c85

Please sign in to comment.