Skip to content

Commit

Permalink
refactor(x/staking): migrate historicalInfo to use collections (#17063
Browse files Browse the repository at this point in the history
)
  • Loading branch information
atheeshp authored Jul 25, 2023
1 parent 32ef636 commit c74e288
Show file tree
Hide file tree
Showing 12 changed files with 42 additions and 170 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ Ref: https://keepachangelog.com/en/1.0.0/

### API Breaking Changes

* (x/slashing) [17063](https://github.com/cosmos/cosmos-sdk/pull/17063) Use collections for `HistoricalInfo`:
* remove `Keeper`: `GetHistoricalInfo`, `SetHistoricalInfo`,
* (x/staking) [17062](https://github.com/cosmos/cosmos-sdk/pull/17062) Use collections for `ValidatorUpdates`:
* remove `Keeper`: `SetValidatorUpdates`, `GetValidatorUpdates`
* (x/slashing) [17023](https://github.com/cosmos/cosmos-sdk/pull/17023) Use collections for `ValidatorSigningInfo`:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -669,10 +669,10 @@ func TestGRPCHistoricalInfo(t *testing.T) {

height := rapid.Int64Min(0).Draw(rt, "height")

assert.NilError(t, f.stakingKeeper.SetHistoricalInfo(
assert.NilError(t, f.stakingKeeper.HistoricalInfo.Set(
f.ctx,
height,
&historicalInfo,
uint64(height),
historicalInfo,
))

req := &stakingtypes.QueryHistoricalInfoRequest{
Expand All @@ -693,10 +693,10 @@ func TestGRPCHistoricalInfo(t *testing.T) {

height := int64(127)

assert.NilError(t, f.stakingKeeper.SetHistoricalInfo(
assert.NilError(t, f.stakingKeeper.HistoricalInfo.Set(
f.ctx,
height,
&historicalInfo,
uint64(height),
historicalInfo,
))

req := &stakingtypes.QueryHistoricalInfoRequest{
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/staking/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func createValidatorAccs(t *testing.T, f *fixture) ([]sdk.AccAddress, []types.Va
sortedVals := make([]types.Validator, len(validators))
copy(sortedVals, validators)
hi := types.NewHistoricalInfo(header, sortedVals, f.stakingKeeper.PowerReduction(f.sdkCtx))
assert.NilError(t, f.stakingKeeper.SetHistoricalInfo(f.sdkCtx, 5, &hi))
assert.NilError(t, f.stakingKeeper.HistoricalInfo.Set(f.sdkCtx, uint64(5), hi))

return addrs, validators
}
Expand Down Expand Up @@ -731,7 +731,7 @@ func TestGRPCQueryHistoricalInfo(t *testing.T) {
qr := f.app.QueryHelper()
queryClient := types.NewQueryClient(qr)

hi, found := f.stakingKeeper.GetHistoricalInfo(ctx, 5)
hi, found := f.stakingKeeper.HistoricalInfo.Get(ctx, uint64(5))
assert.Assert(t, found)

var req *types.QueryHistoricalInfoRequest
Expand Down
2 changes: 1 addition & 1 deletion x/staking/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ func (k Querier) HistoricalInfo(ctx context.Context, req *types.QueryHistoricalI
return nil, status.Error(codes.InvalidArgument, "height cannot be negative")
}

hi, err := k.GetHistoricalInfo(ctx, req.Height)
hi, err := k.Keeper.HistoricalInfo.Get(ctx, uint64(req.Height))
if err != nil {
return nil, status.Errorf(codes.NotFound, "historical info for height %d not found", req.Height)
}
Expand Down
81 changes: 5 additions & 76 deletions x/staking/keeper/historical_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,83 +4,12 @@ import (
"context"
"errors"

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

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)

// GetHistoricalInfo gets the historical info at a given height
func (k Keeper) GetHistoricalInfo(ctx context.Context, height int64) (types.HistoricalInfo, error) {
store := k.storeService.OpenKVStore(ctx)
key := types.GetHistoricalInfoKey(height)

value, err := store.Get(key)
if err != nil {
return types.HistoricalInfo{}, err
}

if value == nil {
return types.HistoricalInfo{}, types.ErrNoHistoricalInfo
}

return types.UnmarshalHistoricalInfo(k.cdc, value)
}

// SetHistoricalInfo sets the historical info at a given height
func (k Keeper) SetHistoricalInfo(ctx context.Context, height int64, hi *types.HistoricalInfo) error {
store := k.storeService.OpenKVStore(ctx)
key := types.GetHistoricalInfoKey(height)
value, err := k.cdc.Marshal(hi)
if err != nil {
return err
}
return store.Set(key, value)
}

// DeleteHistoricalInfo deletes the historical info at a given height
func (k Keeper) DeleteHistoricalInfo(ctx context.Context, height int64) error {
store := k.storeService.OpenKVStore(ctx)
key := types.GetHistoricalInfoKey(height)

return store.Delete(key)
}

// IterateHistoricalInfo provides an iterator over all stored HistoricalInfo
// objects. For each HistoricalInfo object, cb will be called. If the cb returns
// true, the iterator will break and close.
func (k Keeper) IterateHistoricalInfo(ctx context.Context, cb func(types.HistoricalInfo) bool) error {
store := k.storeService.OpenKVStore(ctx)
iterator, err := store.Iterator(types.HistoricalInfoKey, storetypes.PrefixEndBytes(types.HistoricalInfoKey))
if err != nil {
return err
}
defer iterator.Close()

for ; iterator.Valid(); iterator.Next() {
histInfo, err := types.UnmarshalHistoricalInfo(k.cdc, iterator.Value())
if err != nil {
return err
}
if cb(histInfo) {
break
}
}

return nil
}

// GetAllHistoricalInfo returns all stored HistoricalInfo objects.
func (k Keeper) GetAllHistoricalInfo(ctx context.Context) ([]types.HistoricalInfo, error) {
var infos []types.HistoricalInfo
err := k.IterateHistoricalInfo(ctx, func(histInfo types.HistoricalInfo) bool {
infos = append(infos, histInfo)
return false
})

return infos, err
}

// TrackHistoricalInfo saves the latest historical-info and deletes the oldest
// heights that are below pruning height
func (k Keeper) TrackHistoricalInfo(ctx context.Context) error {
Expand All @@ -99,14 +28,14 @@ func (k Keeper) TrackHistoricalInfo(ctx context.Context) error {
// over the historical entries starting from the most recent version to be pruned
// and then return at the first empty entry.
for i := sdkCtx.BlockHeight() - int64(entryNum); i >= 0; i-- {
_, err := k.GetHistoricalInfo(ctx, i)
_, err := k.HistoricalInfo.Get(ctx, uint64(i))
if err != nil {
if errors.Is(err, types.ErrNoHistoricalInfo) {
if errors.Is(err, collections.ErrNotFound) {
break
}
return err
}
if err = k.DeleteHistoricalInfo(ctx, i); err != nil {
if err = k.HistoricalInfo.Remove(ctx, uint64(i)); err != nil {
return err
}
}
Expand All @@ -125,5 +54,5 @@ func (k Keeper) TrackHistoricalInfo(ctx context.Context) error {
historicalEntry := types.NewHistoricalInfo(sdkCtx.BlockHeader(), lastVals, k.PowerReduction(ctx))

// Set latest HistoricalInfo at current height
return k.SetHistoricalInfo(ctx, sdkCtx.BlockHeight(), &historicalEntry)
return k.HistoricalInfo.Set(ctx, uint64(sdkCtx.BlockHeight()), historicalEntry)
}
40 changes: 23 additions & 17 deletions x/staking/keeper/historical_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper_test
import (
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"

"cosmossdk.io/collections"
"cosmossdk.io/math"

"github.com/cosmos/cosmos-sdk/x/staking/testutil"
Expand Down Expand Up @@ -33,17 +34,17 @@ func (s *KeeperTestSuite) TestHistoricalInfo() {
}

hi := stakingtypes.NewHistoricalInfo(ctx.BlockHeader(), validators, keeper.PowerReduction(ctx))
require.NoError(keeper.SetHistoricalInfo(ctx, 2, &hi))
require.NoError(keeper.HistoricalInfo.Set(ctx, uint64(2), hi))

recv, err := keeper.GetHistoricalInfo(ctx, 2)
require.NoError(err, "HistoricalInfo not found after set")
recv, err := keeper.HistoricalInfo.Get(ctx, uint64(2))
require.NoError(err, "HistoricalInfo found after set")
require.Equal(hi, recv, "HistoricalInfo not equal")
require.True(IsValSetSorted(recv.Valset, keeper.PowerReduction(ctx)), "HistoricalInfo validators is not sorted")

require.NoError(keeper.DeleteHistoricalInfo(ctx, 2))
require.NoError(keeper.HistoricalInfo.Remove(ctx, uint64(2)))

recv, err = keeper.GetHistoricalInfo(ctx, 2)
require.ErrorIs(err, stakingtypes.ErrNoHistoricalInfo, "HistoricalInfo found after delete")
recv, err = keeper.HistoricalInfo.Get(ctx, uint64(2))
require.ErrorIs(err, collections.ErrNotFound, "HistoricalInfo not found after delete")
require.Equal(stakingtypes.HistoricalInfo{}, recv, "HistoricalInfo is not empty")
}

Expand Down Expand Up @@ -74,12 +75,12 @@ func (s *KeeperTestSuite) TestTrackHistoricalInfo() {
}
hi4 := stakingtypes.NewHistoricalInfo(h4, valSet, keeper.PowerReduction(ctx))
hi5 := stakingtypes.NewHistoricalInfo(h5, valSet, keeper.PowerReduction(ctx))
require.NoError(keeper.SetHistoricalInfo(ctx, 4, &hi4))
require.NoError(keeper.SetHistoricalInfo(ctx, 5, &hi5))
recv, err := keeper.GetHistoricalInfo(ctx, 4)
require.NoError(keeper.HistoricalInfo.Set(ctx, uint64(4), hi4))
require.NoError(keeper.HistoricalInfo.Set(ctx, uint64(5), hi5))
recv, err := keeper.HistoricalInfo.Get(ctx, uint64(4))
require.NoError(err)
require.Equal(hi4, recv)
recv, err = keeper.GetHistoricalInfo(ctx, 5)
recv, err = keeper.HistoricalInfo.Get(ctx, uint64(5))
require.NoError(err)
require.Equal(hi5, recv)

Expand Down Expand Up @@ -112,16 +113,16 @@ func (s *KeeperTestSuite) TestTrackHistoricalInfo() {
Header: header,
Valset: vals,
}
recv, err = keeper.GetHistoricalInfo(ctx, 10)
recv, err = keeper.HistoricalInfo.Get(ctx, uint64(10))
require.NoError(err, "GetHistoricalInfo failed after BeginBlock")
require.Equal(expected, recv, "GetHistoricalInfo returned unexpected result")

// Check HistoricalInfo at height 5, 4 is pruned
recv, err = keeper.GetHistoricalInfo(ctx, 4)
require.ErrorIs(err, stakingtypes.ErrNoHistoricalInfo, "GetHistoricalInfo did not prune earlier height")
recv, err = keeper.HistoricalInfo.Get(ctx, uint64(4))
require.ErrorIs(err, collections.ErrNotFound, "GetHistoricalInfo did not prune earlier height")
require.Equal(stakingtypes.HistoricalInfo{}, recv, "GetHistoricalInfo at height 4 is not empty after prune")
recv, err = keeper.GetHistoricalInfo(ctx, 5)
require.ErrorIs(err, stakingtypes.ErrNoHistoricalInfo, "GetHistoricalInfo did not prune first prune height")
recv, err = keeper.HistoricalInfo.Get(ctx, uint64(5))
require.ErrorIs(err, collections.ErrNotFound, "GetHistoricalInfo did not prune first prune height")
require.Equal(stakingtypes.HistoricalInfo{}, recv, "GetHistoricalInfo at height 5 is not empty after prune")
}

Expand All @@ -147,10 +148,15 @@ func (s *KeeperTestSuite) TestGetAllHistoricalInfo() {
expHistInfos := []stakingtypes.HistoricalInfo{hist1, hist2, hist3}

for i, hi := range expHistInfos {
require.NoError(keeper.SetHistoricalInfo(ctx, int64(9+i), &hi)) //nolint:gosec // G601: Implicit memory aliasing in for loop.
require.NoError(keeper.HistoricalInfo.Set(ctx, uint64(int64(9+i)), hi))
}

infos, err := keeper.GetAllHistoricalInfo(ctx)
var infos []stakingtypes.HistoricalInfo
err := keeper.HistoricalInfo.Walk(ctx, nil, func(key uint64, info stakingtypes.HistoricalInfo) (stop bool, err error) {
infos = append(infos, info)
return false, nil
})

require.NoError(err)
require.Equal(expHistInfos, infos)
}
2 changes: 2 additions & 0 deletions x/staking/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type Keeper struct {
consensusAddressCodec addresscodec.Codec

Schema collections.Schema
HistoricalInfo collections.Map[uint64, types.HistoricalInfo]
LastTotalPower collections.Item[math.Int]
ValidatorUpdates collections.Item[types.ValidatorUpdates]
}
Expand Down Expand Up @@ -76,6 +77,7 @@ func NewKeeper(
validatorAddressCodec: validatorAddressCodec,
consensusAddressCodec: consensusAddressCodec,
LastTotalPower: collections.NewItem(sb, types.LastTotalPowerKey, "last_total_power", sdk.IntValue),
HistoricalInfo: collections.NewMap(sb, types.HistoricalInfoKey, "historical_info", collections.Uint64Key, codec.CollValue[types.HistoricalInfo](cdc)),
ValidatorUpdates: collections.NewItem(sb, types.ValidatorUpdatesKey, "validator_updates", codec.CollValue[types.ValidatorUpdates](cdc)),
}

Expand Down
1 change: 0 additions & 1 deletion x/staking/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ var (
ErrBothShareMsgsGiven = errors.Register(ModuleName, 35, "both shares amount and shares percent provided")
ErrNeitherShareMsgsGiven = errors.Register(ModuleName, 36, "neither shares amount nor shares percent provided")
ErrInvalidHistoricalInfo = errors.Register(ModuleName, 37, "invalid historical info")
ErrNoHistoricalInfo = errors.Register(ModuleName, 38, "no historical info found")
ErrEmptyValidatorPubKey = errors.Register(ModuleName, 39, "empty validator public key")
ErrCommissionLTMinRate = errors.Register(ModuleName, 40, "commission cannot be less than min rate")
ErrUnbondingNotFound = errors.Register(ModuleName, 41, "unbonding operation not found")
Expand Down
17 changes: 0 additions & 17 deletions x/staking/types/historical_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"cosmossdk.io/errors"
"cosmossdk.io/math"

"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
)

Expand All @@ -27,22 +26,6 @@ func NewHistoricalInfo(header cmtproto.Header, valSet Validators, powerReduction
}
}

// MustUnmarshalHistoricalInfo wll unmarshal historical info and panic on error
func MustUnmarshalHistoricalInfo(cdc codec.BinaryCodec, value []byte) HistoricalInfo {
hi, err := UnmarshalHistoricalInfo(cdc, value)
if err != nil {
panic(err)
}

return hi
}

// UnmarshalHistoricalInfo will unmarshal historical info and return any error
func UnmarshalHistoricalInfo(cdc codec.BinaryCodec, value []byte) (hi HistoricalInfo, err error) {
err = cdc.Unmarshal(value, &hi)
return hi, err
}

// ValidateBasic will ensure HistoricalInfo is not nil and sorted
func ValidateBasic(hi HistoricalInfo) error {
if len(hi.Valset) == 0 {
Expand Down
22 changes: 0 additions & 22 deletions x/staking/types/historical_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import (
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/stretchr/testify/require"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/legacy"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
Expand All @@ -28,26 +26,6 @@ func createValidators(t *testing.T) []types.Validator {
}
}

func TestHistoricalInfo(t *testing.T) {
validators := createValidators(t)
hi := types.NewHistoricalInfo(header, validators, sdk.DefaultPowerReduction)
require.True(t, sort.IsSorted(types.Validators(hi.Valset)), "Validators are not sorted")

var value []byte
require.NotPanics(t, func() {
value = legacy.Cdc.MustMarshal(&hi)
})
require.NotNil(t, value, "Marshaled HistoricalInfo is nil")

recv, err := types.UnmarshalHistoricalInfo(codec.NewAminoCodec(legacy.Cdc), value)
require.Nil(t, err, "Unmarshalling HistoricalInfo failed")
require.Equal(t, hi.Header, recv.Header)
for i := range hi.Valset {
require.True(t, hi.Valset[i].Equal(&recv.Valset[i]))
}
require.True(t, sort.IsSorted(types.Validators(hi.Valset)), "Validators are not sorted")
}

func TestValidateBasic(t *testing.T) {
validators := createValidators(t)
hi := types.HistoricalInfo{
Expand Down
9 changes: 1 addition & 8 deletions x/staking/types/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ var (
RedelegationQueueKey = []byte{0x42} // prefix for the timestamps in redelegations queue
ValidatorQueueKey = []byte{0x43} // prefix for the timestamps in validator queue

HistoricalInfoKey = []byte{0x50} // prefix for the historical info
HistoricalInfoKey = collections.NewPrefix(80) // prefix for the historical info
ValidatorUpdatesKey = collections.NewPrefix(97) // prefix for the end block validator updates key

ParamsKey = []byte{0x51} // prefix for parameters for module x/staking
Expand Down Expand Up @@ -418,10 +418,3 @@ func GetREDsToValDstIndexKey(valDstAddr sdk.ValAddress) []byte {
func GetREDsByDelToValDstIndexKey(delAddr sdk.AccAddress, valDstAddr sdk.ValAddress) []byte {
return append(GetREDsToValDstIndexKey(valDstAddr), address.MustLengthPrefix(delAddr)...)
}

// GetHistoricalInfoKey returns a key prefix for indexing HistoricalInfo objects.
func GetHistoricalInfoKey(height int64) []byte {
heightBytes := make([]byte, 8)
binary.BigEndian.PutUint64(heightBytes, uint64(height))
return append(HistoricalInfoKey, heightBytes...)
}
Loading

0 comments on commit c74e288

Please sign in to comment.