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

refactor(x/slashing): migrate ValidatorMissedBlockBitmap to collections #17568

Merged
merged 12 commits into from
Aug 30, 2023
Merged
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ Ref: https://keepachangelog.com/en/1.0.0/

### API Breaking Changes

* (x/slashing) [#17568](https://github.com/cosmos/cosmos-sdk/pull/17568) Use collections for `ValidatorMissedBlockBitmap`:
* remove from `types`: `ValidatorMissedBlockBitmapPrefixKey`, `ValidatorMissedBlockBitmapKey`
* (x/staking) [#17481](https://github.com/cosmos/cosmos-sdk/pull/17481) Use collections for `UnbondingQueue`:
* remove from `Keeper`: `UBDQueueIterator`
* remove from `types`: `GetUnbondingDelegationTimeKey`
Expand Down
18 changes: 13 additions & 5 deletions x/slashing/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ type Keeper struct {

// the address capable of executing a MsgUpdateParams message. Typically, this
// should be the x/gov module account.
authority string
Schema collections.Schema
Params collections.Item[types.Params]
ValidatorSigningInfo collections.Map[sdk.ConsAddress, types.ValidatorSigningInfo]
AddrPubkeyRelation collections.Map[[]byte, cryptotypes.PubKey]
authority string
Schema collections.Schema
Params collections.Item[types.Params]
ValidatorSigningInfo collections.Map[sdk.ConsAddress, types.ValidatorSigningInfo]
AddrPubkeyRelation collections.Map[[]byte, cryptotypes.PubKey]
ValidatorMissedBlockBitmap collections.Map[collections.Pair[[]byte, uint64], []byte]
}

// NewKeeper creates a slashing keeper
Expand All @@ -56,6 +57,13 @@ func NewKeeper(cdc codec.BinaryCodec, legacyAmino *codec.LegacyAmino, storeServi
sdk.LengthPrefixedBytesKey, // sdk.LengthPrefixedBytesKey is needed to retain state compatibility
codec.CollInterfaceValue[cryptotypes.PubKey](cdc),
),
ValidatorMissedBlockBitmap: collections.NewMap(
sb,
types.ValidatorMissedBlockBitmapKeyPrefix,
"validator_missed_block_bitmap",
collections.PairKeyCodec(sdk.LengthPrefixedBytesKey, collections.Uint64Key),
collections.BytesValue,
),
}

schema, err := sb.Build()
Expand Down
43 changes: 43 additions & 0 deletions x/slashing/keeper/keeper_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keeper_test

import (
"encoding/binary"
"testing"

cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
Expand All @@ -18,6 +19,7 @@ import (
sdktestutil "github.com/cosmos/cosmos-sdk/testutil"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
addresstypes "github.com/cosmos/cosmos-sdk/types/address"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
Expand All @@ -36,10 +38,12 @@ type KeeperTestSuite struct {
slashingKeeper slashingkeeper.Keeper
queryClient slashingtypes.QueryClient
msgServer slashingtypes.MsgServer
key *storetypes.KVStoreKey
}

func (s *KeeperTestSuite) SetupTest() {
key := storetypes.NewKVStoreKey(slashingtypes.StoreKey)
s.key = key
storeService := runtime.NewKVStoreService(key)
testCtx := sdktestutil.DefaultContextWithDB(s.T(), key, storetypes.NewTransientStoreKey("transient_test"))
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: cmttime.Now()})
Expand Down Expand Up @@ -131,6 +135,45 @@ func (s *KeeperTestSuite) TestJailAndSlashWithInfractionReason() {
s.Require().NoError(s.slashingKeeper.Jail(s.ctx, consAddr))
}

// ValidatorMissedBlockBitmapKey returns the key for a validator's missed block
// bitmap chunk.
func validatorMissedBlockBitmapKey(v sdk.ConsAddress, chunkIndex int64) []byte {
bz := make([]byte, 8)
binary.LittleEndian.PutUint64(bz, uint64(chunkIndex))

validatorMissedBlockBitmapKeyPrefix := []byte{0x02} // Prefix for missed block bitmap
return append(append(validatorMissedBlockBitmapKeyPrefix, addresstypes.MustLengthPrefix(v.Bytes())...), bz...)
}

func (s *KeeperTestSuite) TestValidatorMissedBlockBMMigrationToColls() {
s.SetupTest()

consAddr := sdk.ConsAddress(sdk.AccAddress([]byte("addr1_______________")))
index := int64(0)
err := sdktestutil.DiffCollectionsMigration(
s.ctx,
s.key,
100,
func(i int64) {
s.ctx.KVStore(s.key).Set(validatorMissedBlockBitmapKey(consAddr, index), []byte{})
},
"7ad1f994d45ec9495ae5f990a3fba100c2cc70167a154c33fb43882dc004eafd",
)
s.Require().NoError(err)

err = sdktestutil.DiffCollectionsMigration(
s.ctx,
s.key,
100,
func(i int64) {
err := s.slashingKeeper.SetMissedBlockBitmapChunk(s.ctx, consAddr, index, []byte{})
s.Require().NoError(err)
},
"7ad1f994d45ec9495ae5f990a3fba100c2cc70167a154c33fb43882dc004eafd",
)
s.Require().NoError(err)
}

func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite))
}
79 changes: 47 additions & 32 deletions x/slashing/keeper/signing_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package keeper

import (
"context"
"errors"
"time"

"github.com/bits-and-blooms/bitset"

"cosmossdk.io/errors"
storetypes "cosmossdk.io/store/types"
"cosmossdk.io/collections"
errorsmod "cosmossdk.io/errors"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
Expand All @@ -25,7 +26,7 @@ func (k Keeper) HasValidatorSigningInfo(ctx context.Context, consAddr sdk.ConsAd
func (k Keeper) JailUntil(ctx context.Context, consAddr sdk.ConsAddress, jailTime time.Time) error {
signInfo, err := k.ValidatorSigningInfo.Get(ctx, consAddr)
if err != nil {
return errors.Wrap(err, "cannot jail validator that does not have any signing information")
return errorsmod.Wrap(err, "cannot jail validator that does not have any signing information")
}

signInfo.JailedUntil = jailTime
Expand Down Expand Up @@ -61,17 +62,28 @@ func (k Keeper) IsTombstoned(ctx context.Context, consAddr sdk.ConsAddress) bool
// getMissedBlockBitmapChunk gets the bitmap chunk at the given chunk index for
// a validator's missed block signing window.
func (k Keeper) getMissedBlockBitmapChunk(ctx context.Context, addr sdk.ConsAddress, chunkIndex int64) ([]byte, error) {
store := k.storeService.OpenKVStore(ctx)
chunk, err := store.Get(types.ValidatorMissedBlockBitmapKey(addr, chunkIndex))
consAddr, err := k.sk.ConsensusAddressCodec().StringToBytes(addr.String())
if err != nil {
return nil, err
}
chunk, err := k.ValidatorMissedBlockBitmap.Get(ctx, collections.Join(consAddr, uint64(chunkIndex)))
if err != nil {
if !errors.Is(err, collections.ErrNotFound) {
return nil, err
}
return chunk, nil
}
likhita-809 marked this conversation as resolved.
Show resolved Hide resolved
return chunk, err
}

// setMissedBlockBitmapChunk sets the bitmap chunk at the given chunk index for
// SetMissedBlockBitmapChunk sets the bitmap chunk at the given chunk index for
// a validator's missed block signing window.
func (k Keeper) setMissedBlockBitmapChunk(ctx context.Context, addr sdk.ConsAddress, chunkIndex int64, chunk []byte) error {
store := k.storeService.OpenKVStore(ctx)
key := types.ValidatorMissedBlockBitmapKey(addr, chunkIndex)
return store.Set(key, chunk)
func (k Keeper) SetMissedBlockBitmapChunk(ctx context.Context, addr sdk.ConsAddress, chunkIndex int64, chunk []byte) error {
consAddr, err := k.sk.ConsensusAddressCodec().StringToBytes(addr.String())
if err != nil {
return err
}
return k.ValidatorMissedBlockBitmap.Set(ctx, collections.Join(consAddr, uint64(chunkIndex)), chunk)
}

// GetMissedBlockBitmapValue returns true if a validator missed signing a block
Expand All @@ -87,12 +99,12 @@ func (k Keeper) GetMissedBlockBitmapValue(ctx context.Context, addr sdk.ConsAddr
bs := bitset.New(uint(types.MissedBlockBitmapChunkSize))
chunk, err := k.getMissedBlockBitmapChunk(ctx, addr, chunkIndex)
if err != nil {
return false, errors.Wrapf(err, "failed to get bitmap chunk; index: %d", index)
return false, errorsmod.Wrapf(err, "failed to get bitmap chunk; index: %d", index)
}

if chunk != nil {
if err := bs.UnmarshalBinary(chunk); err != nil {
return false, errors.Wrapf(err, "failed to decode bitmap chunk; index: %d", index)
return false, errorsmod.Wrapf(err, "failed to decode bitmap chunk; index: %d", index)
}
}

Expand All @@ -116,12 +128,12 @@ func (k Keeper) SetMissedBlockBitmapValue(ctx context.Context, addr sdk.ConsAddr
bs := bitset.New(uint(types.MissedBlockBitmapChunkSize))
chunk, err := k.getMissedBlockBitmapChunk(ctx, addr, chunkIndex)
if err != nil {
return errors.Wrapf(err, "failed to get bitmap chunk; index: %d", index)
return errorsmod.Wrapf(err, "failed to get bitmap chunk; index: %d", index)
}

if chunk != nil {
if err := bs.UnmarshalBinary(chunk); err != nil {
return errors.Wrapf(err, "failed to decode bitmap chunk; index: %d", index)
return errorsmod.Wrapf(err, "failed to decode bitmap chunk; index: %d", index)
}
}

Expand All @@ -135,28 +147,30 @@ func (k Keeper) SetMissedBlockBitmapValue(ctx context.Context, addr sdk.ConsAddr

updatedChunk, err := bs.MarshalBinary()
if err != nil {
return errors.Wrapf(err, "failed to encode bitmap chunk; index: %d", index)
return errorsmod.Wrapf(err, "failed to encode bitmap chunk; index: %d", index)
}

return k.setMissedBlockBitmapChunk(ctx, addr, chunkIndex, updatedChunk)
return k.SetMissedBlockBitmapChunk(ctx, addr, chunkIndex, updatedChunk)
}

// DeleteMissedBlockBitmap removes a validator's missed block bitmap from state.
func (k Keeper) DeleteMissedBlockBitmap(ctx context.Context, addr sdk.ConsAddress) error {
store := k.storeService.OpenKVStore(ctx)
prefix := types.ValidatorMissedBlockBitmapPrefixKey(addr)
iter, err := store.Iterator(prefix, storetypes.PrefixEndBytes(prefix))
consAddr, err := k.sk.ConsensusAddressCodec().StringToBytes(addr.String())
if err != nil {
return err
}
defer iter.Close()

for ; iter.Valid(); iter.Next() {
err = store.Delete(iter.Key())
rng := collections.NewPrefixedPairRange[[]byte, uint64](consAddr)
err = k.ValidatorMissedBlockBitmap.Walk(ctx, rng, func(key collections.Pair[[]byte, uint64], value []byte) (bool, error) {
err := k.ValidatorMissedBlockBitmap.Remove(ctx, key)
if err != nil {
return err
return true, err
}
return false, nil
})
if err != nil {
return err
}

return nil
}

Expand All @@ -167,20 +181,17 @@ func (k Keeper) DeleteMissedBlockBitmap(ctx context.Context, addr sdk.ConsAddres
// Note: A callback will only be executed over all bitmap chunks that exist in
// state.
func (k Keeper) IterateMissedBlockBitmap(ctx context.Context, addr sdk.ConsAddress, cb func(index int64, missed bool) (stop bool)) error {
store := k.storeService.OpenKVStore(ctx)
prefix := types.ValidatorMissedBlockBitmapPrefixKey(addr)
iter, err := store.Iterator(prefix, storetypes.PrefixEndBytes(prefix))
consAddr, err := k.sk.ConsensusAddressCodec().StringToBytes(addr.String())
if err != nil {
return err
}
defer iter.Close()

var index int64
for ; iter.Valid(); iter.Next() {
rng := collections.NewPrefixedPairRange[[]byte, uint64](consAddr)
err = k.ValidatorMissedBlockBitmap.Walk(ctx, rng, func(key collections.Pair[[]byte, uint64], value []byte) (bool, error) {
bs := bitset.New(uint(types.MissedBlockBitmapChunkSize))

if err := bs.UnmarshalBinary(iter.Value()); err != nil {
return errors.Wrapf(err, "failed to decode bitmap chunk; index: %v", string(iter.Key()))
if err := bs.UnmarshalBinary(value); err != nil {
return true, errorsmod.Wrapf(err, "failed to decode bitmap chunk; index: %v", key)
}

for i := uint(0); i < types.MissedBlockBitmapChunkSize; i++ {
Expand All @@ -191,6 +202,10 @@ func (k Keeper) IterateMissedBlockBitmap(ctx context.Context, addr sdk.ConsAddre

index++
}
return false, nil
})
if err != nil {
return err
}
return nil
}
Expand Down
3 changes: 0 additions & 3 deletions x/slashing/simulation/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,10 @@ func TestDecodeStore(t *testing.T) {
dec := simulation.NewDecodeStore(cdc)

info := types.NewValidatorSigningInfo(consAddr1, 0, 1, time.Now().UTC(), false, 0)
missed := []byte{1} // we want to display the bytes for simulation diffs

kvPairs := kv.Pairs{
Pairs: []kv.Pair{
{Key: types.ValidatorSigningInfoKey(consAddr1), Value: cdc.MustMarshal(&info)},
{Key: types.ValidatorMissedBlockBitmapKey(consAddr1, 6), Value: missed},
{Key: []byte{0x99}, Value: []byte{0x99}}, // This test should panic
},
}
Expand All @@ -43,7 +41,6 @@ func TestDecodeStore(t *testing.T) {
panics bool
}{
{"ValidatorSigningInfo", fmt.Sprintf("%v\n%v", info, info), false},
{"ValidatorMissedBlockBitArray", fmt.Sprintf("missedA: %v\nmissedB: %v\n", missed, missed), false},
{"other", "", true},
}
for i, tt := range tests {
Expand Down
19 changes: 1 addition & 18 deletions x/slashing/types/keys.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package types

import (
"encoding/binary"

"cosmossdk.io/collections"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -51,26 +49,11 @@ const (
var (
ParamsKey = collections.NewPrefix(0) // Prefix for params key
ValidatorSigningInfoKeyPrefix = collections.NewPrefix(1) // Prefix for signing info
ValidatorMissedBlockBitmapKeyPrefix = []byte{0x02} // Prefix for missed block bitmap
ValidatorMissedBlockBitmapKeyPrefix = collections.NewPrefix(2) // Prefix for missed block bitmap
AddrPubkeyRelationKeyPrefix = collections.NewPrefix(3) // Prefix for address-pubkey relation
)

// ValidatorSigningInfoKey - stored by *Consensus* address (not operator address)
func ValidatorSigningInfoKey(v sdk.ConsAddress) []byte {
return append(ValidatorSigningInfoKeyPrefix, address.MustLengthPrefix(v.Bytes())...)
}

// ValidatorMissedBlockBitmapPrefixKey returns the key prefix for a validator's
// missed block bitmap.
func ValidatorMissedBlockBitmapPrefixKey(v sdk.ConsAddress) []byte {
return append(ValidatorMissedBlockBitmapKeyPrefix, address.MustLengthPrefix(v.Bytes())...)
}

// ValidatorMissedBlockBitmapKey returns the key for a validator's missed block
// bitmap chunk.
func ValidatorMissedBlockBitmapKey(v sdk.ConsAddress, chunkIndex int64) []byte {
bz := make([]byte, 8)
binary.LittleEndian.PutUint64(bz, uint64(chunkIndex))

return append(ValidatorMissedBlockBitmapPrefixKey(v), bz...)
}