refactor: add delegations by validator index (#15731)

Co-authored-by: Marko <marbar3778@yahoo.com>
Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>
This commit is contained in:
atheeshp 2023-04-24 19:44:09 +05:30 committed by GitHub
parent 4d41a87a36
commit 7b5602492c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 452 additions and 33 deletions

View File

@ -110,6 +110,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (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.
* (x/staking) (#15731) (https://github.com/cosmos/cosmos-sdk/pull/15731) Introducing a new index to retrieve the delegations by validator efficiently.
### API Breaking Changes

View File

@ -327,7 +327,7 @@ func TestGRPCValidatorDelegations(t *testing.T) {
ValidatorAddr: validator.OperatorAddress,
}
testdata.DeterministicIterations(f.ctx, t, req, f.queryClient.ValidatorDelegations, 11985, false)
testdata.DeterministicIterations(f.ctx, t, req, f.queryClient.ValidatorDelegations, 14475, false)
}
func TestGRPCValidatorUnbondingDelegations(t *testing.T) {

View File

@ -1,6 +1,15 @@
package keeper_test
import "testing"
import (
"fmt"
"testing"
"cosmossdk.io/simapp"
storetypes "cosmossdk.io/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
func BenchmarkGetValidator(b *testing.B) {
// 900 is the max number we are allowed to use in order to avoid simtestutil.CreateTestPubKeys
@ -27,3 +36,117 @@ func BenchmarkGetValidator(b *testing.B) {
}
}
}
func BenchmarkGetValidatorDelegations(b *testing.B) {
var totalPower int64
powersNumber := 10
powers := make([]int64, powersNumber)
for i := range powers {
powers[i] = int64(i)
totalPower += int64(i)
}
app, ctx, _, valAddrs, vals := initValidators(b, totalPower, len(powers), powers)
for _, validator := range vals {
app.StakingKeeper.SetValidator(ctx, validator)
}
delegationsNum := 1000
for _, val := range valAddrs {
for i := 0; i < delegationsNum; i++ {
delegator := sdk.AccAddress(fmt.Sprintf("address%d", i))
banktestutil.FundAccount(app.BankKeeper, ctx, delegator,
sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(int64(i)))))
NewDel := types.NewDelegation(delegator, val, sdk.NewDec(int64(i)))
app.StakingKeeper.SetDelegation(ctx, NewDel)
}
}
b.ResetTimer()
for n := 0; n < b.N; n++ {
updateValidatorDelegations(ctx, app, valAddrs[0], sdk.ValAddress("val"))
}
}
func BenchmarkGetValidatorDelegationsLegacy(b *testing.B) {
var totalPower int64
powersNumber := 10
powers := make([]int64, powersNumber)
for i := range powers {
powers[i] = int64(i)
totalPower += int64(i)
}
app, ctx, _, valAddrs, vals := initValidators(b, totalPower, len(powers), powers)
for _, validator := range vals {
app.StakingKeeper.SetValidator(ctx, validator)
}
delegationsNum := 1000
for _, val := range valAddrs {
for i := 0; i < delegationsNum; i++ {
delegator := sdk.AccAddress(fmt.Sprintf("address%d", i))
banktestutil.FundAccount(app.BankKeeper, ctx, delegator,
sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(int64(i)))))
NewDel := types.NewDelegation(delegator, val, sdk.NewDec(int64(i)))
app.StakingKeeper.SetDelegation(ctx, NewDel)
}
}
b.ResetTimer()
for n := 0; n < b.N; n++ {
updateValidatorDelegationsLegacy(ctx, app, valAddrs[0], sdk.ValAddress("val"))
}
}
func updateValidatorDelegationsLegacy(ctx sdk.Context, app *simapp.SimApp, existingValAddr, newValAddr sdk.ValAddress) {
storeKey := app.GetKey(types.StoreKey)
cdc, k := app.AppCodec(), app.StakingKeeper
store := ctx.KVStore(storeKey)
iterator := storetypes.KVStorePrefixIterator(store, types.DelegationKey)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
delegation := types.MustUnmarshalDelegation(cdc, iterator.Value())
if delegation.GetValidatorAddr().Equals(existingValAddr) {
k.RemoveDelegation(ctx, delegation)
delegation.ValidatorAddress = newValAddr.String()
k.SetDelegation(ctx, delegation)
}
}
}
func updateValidatorDelegations(ctx sdk.Context, app *simapp.SimApp, existingValAddr, newValAddr sdk.ValAddress) {
storeKey := app.GetKey(types.StoreKey)
cdc, k := app.AppCodec(), app.StakingKeeper
store := ctx.KVStore(storeKey)
itr := storetypes.KVStorePrefixIterator(store, types.GetDelegationsByValPrefixKey(existingValAddr))
defer itr.Close()
for ; itr.Valid(); itr.Next() {
key := itr.Key()
valAddr, delAddr, err := types.ParseDelegationsByValKey(key)
if err != nil {
panic(err)
}
bz := store.Get(types.GetDelegationKey(delAddr, valAddr))
delegation := types.MustUnmarshalDelegation(cdc, bz)
// remove old operator addr from delegation
if err := k.RemoveDelegation(ctx, delegation); err != nil {
panic(err)
}
delegation.ValidatorAddress = newValAddr.String()
// add with new operator addr
k.SetDelegation(ctx, delegation)
}
}

View File

@ -60,14 +60,22 @@ func (k Keeper) GetAllDelegations(ctx sdk.Context) (delegations []types.Delegati
func (k Keeper) GetValidatorDelegations(ctx sdk.Context, valAddr sdk.ValAddress) (delegations []types.Delegation) {
store := ctx.KVStore(k.storeKey)
iterator := storetypes.KVStorePrefixIterator(store, types.DelegationKey)
iterator := storetypes.KVStorePrefixIterator(store, types.GetDelegationsByValPrefixKey(valAddr))
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value())
if delegation.GetValidatorAddr().Equals(valAddr) {
delegations = append(delegations, delegation)
var delegation types.Delegation
valAddr, delAddr, err := types.ParseDelegationsByValKey(iterator.Key())
if err != nil {
panic(err)
}
bz := store.Get(types.GetDelegationKey(delAddr, valAddr))
if err := k.cdc.Unmarshal(bz, &delegation); err != nil {
panic(err)
}
delegations = append(delegations, delegation)
}
return delegations
@ -103,6 +111,9 @@ func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) {
store := ctx.KVStore(k.storeKey)
b := types.MustMarshalDelegation(k.cdc, delegation)
store.Set(types.GetDelegationKey(delegatorAddress, delegation.GetValidatorAddr()), b)
// set the delegation in validator delegator index
store.Set(types.GetDelegationsByValKey(delegation.GetValidatorAddr(), delegatorAddress), []byte{})
}
// RemoveDelegation removes a delegation
@ -119,6 +130,8 @@ func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) e
store := ctx.KVStore(k.storeKey)
store.Delete(types.GetDelegationKey(delegatorAddress, delegation.GetValidatorAddr()))
store.Delete(types.GetDelegationsByValKey(delegation.GetValidatorAddr(), delegatorAddress))
return nil
}

View File

@ -144,6 +144,83 @@ func (s *KeeperTestSuite) TestDelegation() {
require.Equal(0, len(resBonds))
}
func (s *KeeperTestSuite) TestDelegationsByValIndex() {
ctx, keeper := s.ctx, s.stakingKeeper
require := s.Require()
addrDels, valAddrs := createValAddrs(3)
for _, addr := range addrDels {
s.accountKeeper.EXPECT().StringToBytes(addr.String()).Return(addr, nil).AnyTimes()
s.accountKeeper.EXPECT().BytesToString(addr).Return(addr.String(), nil).AnyTimes()
s.bankKeeper.EXPECT().DelegateCoinsFromAccountToModule(gomock.Any(), addr, gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
}
// construct the validators
amts := []math.Int{sdk.NewInt(9), sdk.NewInt(8), sdk.NewInt(7)}
var validators [3]stakingtypes.Validator
for i, amt := range amts {
validators[i] = testutil.NewValidator(s.T(), valAddrs[i], PKs[i])
validators[i], _ = validators[i].AddTokensFromDel(amt)
validators[i] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[i], true)
}
// delegate 2 tokens
//
// total delegations after delegating: del1 -> 2stake
_, err := s.msgServer.Delegate(ctx, stakingtypes.NewMsgDelegate(addrDels[0], valAddrs[0], sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(2))))
require.NoError(err)
dels := s.stakingKeeper.GetValidatorDelegations(ctx, valAddrs[0])
require.Len(dels, 1)
// delegate 4 tokens
//
// total delegations after delegating: del1 -> 2stake, del2 -> 4stake
_, err = s.msgServer.Delegate(ctx, stakingtypes.NewMsgDelegate(addrDels[1], valAddrs[0], sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(4))))
require.NoError(err)
dels = s.stakingKeeper.GetValidatorDelegations(ctx, valAddrs[0])
require.Len(dels, 2)
// undelegate 1 token from del1
//
// total delegations after undelegating: del1 -> 1stake, del2 -> 4stake
_, err = s.msgServer.Undelegate(ctx, stakingtypes.NewMsgUndelegate(addrDels[0], valAddrs[0], sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))))
require.NoError(err)
dels = s.stakingKeeper.GetValidatorDelegations(ctx, valAddrs[0])
require.Len(dels, 2)
// undelegate 1 token from del1
//
// total delegations after undelegating: del2 -> 4stake
_, err = s.msgServer.Undelegate(ctx, stakingtypes.NewMsgUndelegate(addrDels[0], valAddrs[0], sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))))
require.NoError(err)
dels = s.stakingKeeper.GetValidatorDelegations(ctx, valAddrs[0])
require.Len(dels, 1)
// undelegate 2 tokens from del2
//
// total delegations after undelegating: del2 -> 2stake
_, err = s.msgServer.Undelegate(ctx, stakingtypes.NewMsgUndelegate(addrDels[1], valAddrs[0], sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(2))))
require.NoError(err)
dels = s.stakingKeeper.GetValidatorDelegations(ctx, valAddrs[0])
require.Len(dels, 1)
// undelegate 2 tokens from del2
//
// total delegations after undelegating: []
_, err = s.msgServer.Undelegate(ctx, stakingtypes.NewMsgUndelegate(addrDels[1], valAddrs[0], sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(2))))
require.NoError(err)
dels = s.stakingKeeper.GetValidatorDelegations(ctx, valAddrs[0])
require.Len(dels, 0)
}
// tests Get/Set/Remove UnbondingDelegation
func (s *KeeperTestSuite) TestUnbondingDelegation() {
ctx, keeper := s.ctx, s.stakingKeeper

View File

@ -92,11 +92,61 @@ func (k Querier) ValidatorDelegations(c context.Context, req *types.QueryValidat
if req.ValidatorAddr == "" {
return nil, status.Error(codes.InvalidArgument, "validator address cannot be empty")
}
valAddr, err := sdk.ValAddressFromBech32(req.ValidatorAddr)
if err != nil {
return nil, err
}
ctx := sdk.UnwrapSDKContext(c)
store := ctx.KVStore(k.storeKey)
delStore := prefix.NewStore(store, types.GetDelegationsByValPrefixKey(valAddr))
var (
dels types.Delegations
pageRes *query.PageResponse
)
pageRes, err = query.Paginate(delStore, req.Pagination, func(delAddr, value []byte) error {
bz := store.Get(types.GetDelegationKey(delAddr, valAddr))
var delegation types.Delegation
err = k.cdc.Unmarshal(bz, &delegation)
if err != nil {
return err
}
dels = append(dels, delegation)
return nil
})
if err != nil {
delegations, pageResponse, err := k.getValidatorDelegationsLegacy(ctx, req)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
dels = types.Delegations{}
for _, d := range delegations {
dels = append(dels, *d)
}
pageRes = pageResponse
}
delResponses, err := DelegationsToDelegationResponses(ctx, k.Keeper, dels)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
return &types.QueryValidatorDelegationsResponse{
DelegationResponses: delResponses, Pagination: pageRes,
}, nil
}
func (k Querier) getValidatorDelegationsLegacy(ctx sdk.Context, req *types.QueryValidatorDelegationsRequest) ([]*types.Delegation, *query.PageResponse, error) {
store := ctx.KVStore(k.storeKey)
valStore := prefix.NewStore(store, types.DelegationKey)
delegations, pageRes, err := query.GenericFilteredPaginate(k.cdc, valStore, req.Pagination, func(key []byte, delegation *types.Delegation) (*types.Delegation, error) {
return query.GenericFilteredPaginate(k.cdc, valStore, req.Pagination, func(key []byte, delegation *types.Delegation) (*types.Delegation, error) {
valAddr, err := sdk.ValAddressFromBech32(req.ValidatorAddr)
if err != nil {
return nil, err
@ -110,23 +160,6 @@ func (k Querier) ValidatorDelegations(c context.Context, req *types.QueryValidat
}, func() *types.Delegation {
return &types.Delegation{}
})
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
dels := types.Delegations{}
for _, d := range delegations {
dels = append(dels, *d)
}
delResponses, err := DelegationsToDelegationResponses(ctx, k.Keeper, dels)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
return &types.QueryValidatorDelegationsResponse{
DelegationResponses: delResponses, Pagination: pageRes,
}, nil
}
// ValidatorUnbondingDelegations queries unbonding delegations of a validator

View File

@ -40,5 +40,5 @@ func (m Migrator) Migrate3to4(ctx sdk.Context) error {
// Migrate4to5 migrates x/staking state from consensus version 4 to 5.
func (m Migrator) Migrate4to5(ctx sdk.Context) error {
return v5.MigrateStore(ctx, m.keeper.storeKey)
return v5.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc)
}

View File

@ -1,8 +1,57 @@
package v5
import "encoding/binary"
import (
"bytes"
"encoding/binary"
"fmt"
var HistoricalInfoKey = []byte{0x50} // prefix for the historical info
sdk "github.com/cosmos/cosmos-sdk/types"
)
const (
// ModuleName is the name of the module
ModuleName = "staking"
)
var (
DelegationKey = []byte{0x31} // key for a delegation
DelegationByValIndexKey = []byte{0x37} // key for delegations by a validator
HistoricalInfoKey = []byte{0x50} // prefix for the historical info
)
// ParseDelegationKey parses given key and returns delagator, validator address bytes
func ParseDelegationKey(bz []byte) (sdk.AccAddress, sdk.ValAddress, error) {
prefixLength := len(DelegationKey)
if prefix := bz[:prefixLength]; !bytes.Equal(prefix, DelegationKey) {
return nil, nil, fmt.Errorf("invalid prefix; expected: %X, got: %x", DelegationKey, prefix)
}
bz = bz[prefixLength:] // remove the prefix byte
if len(bz) == 0 {
return nil, nil, fmt.Errorf("no bytes left to parse: %X", bz)
}
delAddrLen := bz[0]
bz = bz[1:] // remove the length byte of delegator address.
if len(bz) == 0 {
return nil, nil, fmt.Errorf("no bytes left to parse delegator address: %X", bz)
}
del := bz[:int(delAddrLen)]
bz = bz[int(delAddrLen):] // remove the length byte of a delegator address
if len(bz) == 0 {
return nil, nil, fmt.Errorf("no bytes left to parse delegator address: %X", bz)
}
bz = bz[1:] // remove the validator address bytes.
if len(bz) == 0 {
return nil, nil, fmt.Errorf("no bytes left to parse validator address: %X", bz)
}
val := bz
return del, val, nil
}
// GetHistoricalInfoKey returns a key prefix for indexing HistoricalInfo objects.
func GetHistoricalInfoKey(height int64) []byte {

View File

@ -8,11 +8,16 @@ import (
storetypes "cosmossdk.io/store/types"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/testutil"
"github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
"github.com/cosmos/cosmos-sdk/x/staking"
v2 "github.com/cosmos/cosmos-sdk/x/staking/migrations/v2"
v5 "github.com/cosmos/cosmos-sdk/x/staking/migrations/v5"
stackingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -56,7 +61,7 @@ func TestHistoricalKeysMigration(t *testing.T) {
}
// migrate store to new key format
require.NoErrorf(t, v5.MigrateStore(ctx, storeKey), "v5.MigrateStore failed, seed: %d", seed)
require.NoErrorf(t, v5.MigrateStore(ctx, storeKey, cdc), "v5.MigrateStore failed, seed: %d", seed)
// check results
for _, tc := range testCases {
@ -66,6 +71,58 @@ func TestHistoricalKeysMigration(t *testing.T) {
}
}
func createHistoricalInfo(height int64, chainID string) *stackingtypes.HistoricalInfo {
return &stackingtypes.HistoricalInfo{Header: cmtproto.Header{ChainID: chainID, Height: height}}
func createHistoricalInfo(height int64, chainID string) *stakingtypes.HistoricalInfo {
return &stakingtypes.HistoricalInfo{Header: cmtproto.Header{ChainID: chainID, Height: height}}
}
func TestDelegationsByValidatorMigrations(t *testing.T) {
cdc := moduletestutil.MakeTestEncodingConfig(staking.AppModuleBasic{}).Codec
storeKey := storetypes.NewKVStoreKey(v5.ModuleName)
tKey := storetypes.NewTransientStoreKey("transient_test")
ctx := testutil.DefaultContext(storeKey, tKey)
store := ctx.KVStore(storeKey)
accAddrs := sims.CreateIncrementalAccounts(11)
valAddrs := sims.ConvertAddrsToValAddrs(accAddrs[0:1])
var addedDels []stakingtypes.Delegation
for i := 1; i < 11; i++ {
del1 := stakingtypes.NewDelegation(accAddrs[i], valAddrs[0], sdk.NewDec(100))
store.Set(stakingtypes.GetDelegationKey(accAddrs[i], valAddrs[0]), stakingtypes.MustMarshalDelegation(cdc, del1))
addedDels = append(addedDels, del1)
}
// before migration the state of delegations by val index should be empty
dels := getValDelegations(ctx, cdc, storeKey, valAddrs[0])
assert.Len(t, dels, 0)
err := v5.MigrateStore(ctx, storeKey, cdc)
assert.NoError(t, err)
// after migration the state of delegations by val index should not be empty
dels = getValDelegations(ctx, cdc, storeKey, valAddrs[0])
assert.Len(t, dels, len(addedDels))
assert.Equal(t, addedDels, dels)
}
func getValDelegations(ctx sdk.Context, cdc codec.Codec, storeKey storetypes.StoreKey, valAddr sdk.ValAddress) []stakingtypes.Delegation {
var delegations []stakingtypes.Delegation
store := ctx.KVStore(storeKey)
iterator := storetypes.KVStorePrefixIterator(store, stakingtypes.GetDelegationsByValPrefixKey(valAddr))
for ; iterator.Valid(); iterator.Next() {
var delegation stakingtypes.Delegation
valAddr, delAddr, err := stakingtypes.ParseDelegationsByValKey(iterator.Key())
if err != nil {
panic(err)
}
bz := store.Get(stakingtypes.GetDelegationKey(delAddr, valAddr))
cdc.MustUnmarshal(bz, &delegation)
delegations = append(delegations, delegation)
}
return delegations
}

View File

@ -6,13 +6,37 @@ import (
"cosmossdk.io/log"
"cosmossdk.io/store/prefix"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
// MigrateStore performs in-place store migrations from v4 to v5.
func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey) error {
func migrateDelegationsByValidatorIndex(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec) error {
store := ctx.KVStore(storeKey)
iterator := storetypes.KVStorePrefixIterator(store, DelegationKey)
for ; iterator.Valid(); iterator.Next() {
key := iterator.Key()
del, val, err := ParseDelegationKey(key)
if err != nil {
return err
}
store.Set(types.GetDelegationsByValKey(val, del), []byte{})
}
return nil
}
// MigrateStore performs in-place store migrations from v4 to v5.
func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec) error {
store := ctx.KVStore(storeKey)
if err := migrateDelegationsByValidatorIndex(ctx, storeKey, cdc); err != nil {
return err
}
return migrateHistoricalInfoKeys(store, ctx.Logger())
}

View File

@ -40,6 +40,7 @@ var (
RedelegationKey = []byte{0x34} // key for a redelegation
RedelegationByValSrcIndexKey = []byte{0x35} // prefix for each key for an redelegation, by source validator operator
RedelegationByValDstIndexKey = []byte{0x36} // prefix for each key for an redelegation, by destination validator operator
DelegationByValIndexKey = []byte{0x37} // key for delegations by a validator
UnbondingIDKey = []byte{0x37} // key for the counter for the incrementing id for UnbondingOperations
UnbondingIndexKey = []byte{0x38} // prefix for an index for looking up unbonding operations by their IDs
@ -210,6 +211,47 @@ func GetDelegationKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte {
return append(GetDelegationsKey(delAddr), address.MustLengthPrefix(valAddr)...)
}
// GetDelegationsByValKey creates the key for delegations by validator address
// VALUE: staking/Delegation
func GetDelegationsByValKey(valAddr sdk.ValAddress, delAddr sdk.AccAddress) []byte {
return append(GetDelegationsByValPrefixKey(valAddr), delAddr...)
}
// GetDelegationsByValPrefixKey builds a prefix key bytes with the given validator address bytes.
func GetDelegationsByValPrefixKey(valAddr sdk.ValAddress) []byte {
return append(DelegationByValIndexKey, address.MustLengthPrefix(valAddr)...)
}
// ParseDelegationsByValKey parses given key and returns validator, delegator address bytes
func ParseDelegationsByValKey(bz []byte) (sdk.ValAddress, sdk.AccAddress, error) {
prefixLength := len(DelegationByValIndexKey)
if prefix := bz[:prefixLength]; !bytes.Equal(prefix, DelegationByValIndexKey) {
return nil, nil, fmt.Errorf("invalid prefix; expected: %X, got: %x", DelegationByValIndexKey, prefix)
}
bz = bz[prefixLength:] // remove the prefix byte
if len(bz) == 0 {
return nil, nil, fmt.Errorf("no bytes left to parse: %X", bz)
}
valAddrLen := bz[0]
bz = bz[1:] // remove the length byte of validator address.
if len(bz) == 0 {
return nil, nil, fmt.Errorf("no bytes left to parse validator address: %X", bz)
}
val := bz[0:int(valAddrLen)]
bz = bz[int(valAddrLen):] // remove the delegator bytes
if len(bz) == 0 {
return nil, nil, fmt.Errorf("no bytes left to parse delegator address: %X", bz)
}
del := bz
return val, del, nil
}
// GetDelegationsKey creates the prefix for a delegator for all validators
func GetDelegationsKey(delAddr sdk.AccAddress) []byte {
return append(DelegationKey, address.MustLengthPrefix(delAddr)...)