Skip to content

Commit 7b56024

Browse files
atheeshptac0turtlealexanderbez
authored
refactor: add delegations by validator index (#15731)
Co-authored-by: Marko <marbar3778@yahoo.com> Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>
1 parent 4d41a87 commit 7b56024

11 files changed

Lines changed: 449 additions & 30 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
110110
* (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.
111111
* (x/feegrant) [#14294](https://github.com/cosmos/cosmos-sdk/pull/14294) Moved the logic of rejecting duplicate grant from `msg_server` to `keeper` method.
112112
* (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.
113+
* (x/staking) (#15731) (https://github.com/cosmos/cosmos-sdk/pull/15731) Introducing a new index to retrieve the delegations by validator efficiently.
113114

114115
### API Breaking Changes
115116

tests/integration/staking/keeper/determinstic_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ func TestGRPCValidatorDelegations(t *testing.T) {
327327
ValidatorAddr: validator.OperatorAddress,
328328
}
329329

330-
testdata.DeterministicIterations(f.ctx, t, req, f.queryClient.ValidatorDelegations, 11985, false)
330+
testdata.DeterministicIterations(f.ctx, t, req, f.queryClient.ValidatorDelegations, 14475, false)
331331
}
332332

333333
func TestGRPCValidatorUnbondingDelegations(t *testing.T) {

tests/integration/staking/keeper/validator_bench_test.go

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
package keeper_test
22

3-
import "testing"
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"cosmossdk.io/simapp"
8+
storetypes "cosmossdk.io/store/types"
9+
sdk "github.com/cosmos/cosmos-sdk/types"
10+
banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil"
11+
"github.com/cosmos/cosmos-sdk/x/staking/types"
12+
)
413

514
func BenchmarkGetValidator(b *testing.B) {
615
// 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) {
2736
}
2837
}
2938
}
39+
40+
func BenchmarkGetValidatorDelegations(b *testing.B) {
41+
var totalPower int64
42+
powersNumber := 10
43+
44+
powers := make([]int64, powersNumber)
45+
for i := range powers {
46+
powers[i] = int64(i)
47+
totalPower += int64(i)
48+
}
49+
50+
app, ctx, _, valAddrs, vals := initValidators(b, totalPower, len(powers), powers)
51+
for _, validator := range vals {
52+
app.StakingKeeper.SetValidator(ctx, validator)
53+
}
54+
55+
delegationsNum := 1000
56+
for _, val := range valAddrs {
57+
for i := 0; i < delegationsNum; i++ {
58+
delegator := sdk.AccAddress(fmt.Sprintf("address%d", i))
59+
banktestutil.FundAccount(app.BankKeeper, ctx, delegator,
60+
sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(int64(i)))))
61+
NewDel := types.NewDelegation(delegator, val, sdk.NewDec(int64(i)))
62+
app.StakingKeeper.SetDelegation(ctx, NewDel)
63+
}
64+
}
65+
66+
b.ResetTimer()
67+
for n := 0; n < b.N; n++ {
68+
updateValidatorDelegations(ctx, app, valAddrs[0], sdk.ValAddress("val"))
69+
}
70+
}
71+
72+
func BenchmarkGetValidatorDelegationsLegacy(b *testing.B) {
73+
var totalPower int64
74+
powersNumber := 10
75+
76+
powers := make([]int64, powersNumber)
77+
for i := range powers {
78+
powers[i] = int64(i)
79+
totalPower += int64(i)
80+
}
81+
82+
app, ctx, _, valAddrs, vals := initValidators(b, totalPower, len(powers), powers)
83+
84+
for _, validator := range vals {
85+
app.StakingKeeper.SetValidator(ctx, validator)
86+
}
87+
88+
delegationsNum := 1000
89+
for _, val := range valAddrs {
90+
for i := 0; i < delegationsNum; i++ {
91+
delegator := sdk.AccAddress(fmt.Sprintf("address%d", i))
92+
banktestutil.FundAccount(app.BankKeeper, ctx, delegator,
93+
sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(int64(i)))))
94+
NewDel := types.NewDelegation(delegator, val, sdk.NewDec(int64(i)))
95+
app.StakingKeeper.SetDelegation(ctx, NewDel)
96+
}
97+
}
98+
99+
b.ResetTimer()
100+
for n := 0; n < b.N; n++ {
101+
updateValidatorDelegationsLegacy(ctx, app, valAddrs[0], sdk.ValAddress("val"))
102+
}
103+
}
104+
105+
func updateValidatorDelegationsLegacy(ctx sdk.Context, app *simapp.SimApp, existingValAddr, newValAddr sdk.ValAddress) {
106+
storeKey := app.GetKey(types.StoreKey)
107+
cdc, k := app.AppCodec(), app.StakingKeeper
108+
109+
store := ctx.KVStore(storeKey)
110+
111+
iterator := storetypes.KVStorePrefixIterator(store, types.DelegationKey)
112+
defer iterator.Close()
113+
114+
for ; iterator.Valid(); iterator.Next() {
115+
delegation := types.MustUnmarshalDelegation(cdc, iterator.Value())
116+
if delegation.GetValidatorAddr().Equals(existingValAddr) {
117+
k.RemoveDelegation(ctx, delegation)
118+
delegation.ValidatorAddress = newValAddr.String()
119+
k.SetDelegation(ctx, delegation)
120+
}
121+
}
122+
}
123+
124+
func updateValidatorDelegations(ctx sdk.Context, app *simapp.SimApp, existingValAddr, newValAddr sdk.ValAddress) {
125+
storeKey := app.GetKey(types.StoreKey)
126+
cdc, k := app.AppCodec(), app.StakingKeeper
127+
128+
store := ctx.KVStore(storeKey)
129+
130+
itr := storetypes.KVStorePrefixIterator(store, types.GetDelegationsByValPrefixKey(existingValAddr))
131+
defer itr.Close()
132+
133+
for ; itr.Valid(); itr.Next() {
134+
key := itr.Key()
135+
valAddr, delAddr, err := types.ParseDelegationsByValKey(key)
136+
if err != nil {
137+
panic(err)
138+
}
139+
140+
bz := store.Get(types.GetDelegationKey(delAddr, valAddr))
141+
delegation := types.MustUnmarshalDelegation(cdc, bz)
142+
143+
// remove old operator addr from delegation
144+
if err := k.RemoveDelegation(ctx, delegation); err != nil {
145+
panic(err)
146+
}
147+
148+
delegation.ValidatorAddress = newValAddr.String()
149+
// add with new operator addr
150+
k.SetDelegation(ctx, delegation)
151+
}
152+
}

x/staking/keeper/delegation.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,22 @@ func (k Keeper) GetAllDelegations(ctx sdk.Context) (delegations []types.Delegati
6060
func (k Keeper) GetValidatorDelegations(ctx sdk.Context, valAddr sdk.ValAddress) (delegations []types.Delegation) {
6161
store := ctx.KVStore(k.storeKey)
6262

63-
iterator := storetypes.KVStorePrefixIterator(store, types.DelegationKey)
63+
iterator := storetypes.KVStorePrefixIterator(store, types.GetDelegationsByValPrefixKey(valAddr))
6464
defer iterator.Close()
6565

6666
for ; iterator.Valid(); iterator.Next() {
67-
delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value())
68-
if delegation.GetValidatorAddr().Equals(valAddr) {
69-
delegations = append(delegations, delegation)
67+
var delegation types.Delegation
68+
valAddr, delAddr, err := types.ParseDelegationsByValKey(iterator.Key())
69+
if err != nil {
70+
panic(err)
71+
}
72+
73+
bz := store.Get(types.GetDelegationKey(delAddr, valAddr))
74+
if err := k.cdc.Unmarshal(bz, &delegation); err != nil {
75+
panic(err)
7076
}
77+
78+
delegations = append(delegations, delegation)
7179
}
7280

7381
return delegations
@@ -103,6 +111,9 @@ func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) {
103111
store := ctx.KVStore(k.storeKey)
104112
b := types.MustMarshalDelegation(k.cdc, delegation)
105113
store.Set(types.GetDelegationKey(delegatorAddress, delegation.GetValidatorAddr()), b)
114+
115+
// set the delegation in validator delegator index
116+
store.Set(types.GetDelegationsByValKey(delegation.GetValidatorAddr(), delegatorAddress), []byte{})
106117
}
107118

108119
// RemoveDelegation removes a delegation
@@ -119,6 +130,8 @@ func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) e
119130

120131
store := ctx.KVStore(k.storeKey)
121132
store.Delete(types.GetDelegationKey(delegatorAddress, delegation.GetValidatorAddr()))
133+
store.Delete(types.GetDelegationsByValKey(delegation.GetValidatorAddr(), delegatorAddress))
134+
122135
return nil
123136
}
124137

x/staking/keeper/delegation_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,83 @@ func (s *KeeperTestSuite) TestDelegation() {
144144
require.Equal(0, len(resBonds))
145145
}
146146

147+
func (s *KeeperTestSuite) TestDelegationsByValIndex() {
148+
ctx, keeper := s.ctx, s.stakingKeeper
149+
require := s.Require()
150+
151+
addrDels, valAddrs := createValAddrs(3)
152+
153+
for _, addr := range addrDels {
154+
s.accountKeeper.EXPECT().StringToBytes(addr.String()).Return(addr, nil).AnyTimes()
155+
s.accountKeeper.EXPECT().BytesToString(addr).Return(addr.String(), nil).AnyTimes()
156+
s.bankKeeper.EXPECT().DelegateCoinsFromAccountToModule(gomock.Any(), addr, gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
157+
}
158+
159+
// construct the validators
160+
amts := []math.Int{sdk.NewInt(9), sdk.NewInt(8), sdk.NewInt(7)}
161+
var validators [3]stakingtypes.Validator
162+
for i, amt := range amts {
163+
validators[i] = testutil.NewValidator(s.T(), valAddrs[i], PKs[i])
164+
validators[i], _ = validators[i].AddTokensFromDel(amt)
165+
166+
validators[i] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[i], true)
167+
}
168+
169+
// delegate 2 tokens
170+
//
171+
// total delegations after delegating: del1 -> 2stake
172+
_, err := s.msgServer.Delegate(ctx, stakingtypes.NewMsgDelegate(addrDels[0], valAddrs[0], sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(2))))
173+
require.NoError(err)
174+
175+
dels := s.stakingKeeper.GetValidatorDelegations(ctx, valAddrs[0])
176+
require.Len(dels, 1)
177+
178+
// delegate 4 tokens
179+
//
180+
// total delegations after delegating: del1 -> 2stake, del2 -> 4stake
181+
_, err = s.msgServer.Delegate(ctx, stakingtypes.NewMsgDelegate(addrDels[1], valAddrs[0], sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(4))))
182+
require.NoError(err)
183+
184+
dels = s.stakingKeeper.GetValidatorDelegations(ctx, valAddrs[0])
185+
require.Len(dels, 2)
186+
187+
// undelegate 1 token from del1
188+
//
189+
// total delegations after undelegating: del1 -> 1stake, del2 -> 4stake
190+
_, err = s.msgServer.Undelegate(ctx, stakingtypes.NewMsgUndelegate(addrDels[0], valAddrs[0], sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))))
191+
require.NoError(err)
192+
193+
dels = s.stakingKeeper.GetValidatorDelegations(ctx, valAddrs[0])
194+
require.Len(dels, 2)
195+
196+
// undelegate 1 token from del1
197+
//
198+
// total delegations after undelegating: del2 -> 4stake
199+
_, err = s.msgServer.Undelegate(ctx, stakingtypes.NewMsgUndelegate(addrDels[0], valAddrs[0], sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))))
200+
require.NoError(err)
201+
202+
dels = s.stakingKeeper.GetValidatorDelegations(ctx, valAddrs[0])
203+
require.Len(dels, 1)
204+
205+
// undelegate 2 tokens from del2
206+
//
207+
// total delegations after undelegating: del2 -> 2stake
208+
_, err = s.msgServer.Undelegate(ctx, stakingtypes.NewMsgUndelegate(addrDels[1], valAddrs[0], sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(2))))
209+
require.NoError(err)
210+
211+
dels = s.stakingKeeper.GetValidatorDelegations(ctx, valAddrs[0])
212+
require.Len(dels, 1)
213+
214+
// undelegate 2 tokens from del2
215+
//
216+
// total delegations after undelegating: []
217+
_, err = s.msgServer.Undelegate(ctx, stakingtypes.NewMsgUndelegate(addrDels[1], valAddrs[0], sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(2))))
218+
require.NoError(err)
219+
220+
dels = s.stakingKeeper.GetValidatorDelegations(ctx, valAddrs[0])
221+
require.Len(dels, 0)
222+
}
223+
147224
// tests Get/Set/Remove UnbondingDelegation
148225
func (s *KeeperTestSuite) TestUnbondingDelegation() {
149226
ctx, keeper := s.ctx, s.stakingKeeper

x/staking/keeper/grpc_query.go

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -92,31 +92,44 @@ func (k Querier) ValidatorDelegations(c context.Context, req *types.QueryValidat
9292
if req.ValidatorAddr == "" {
9393
return nil, status.Error(codes.InvalidArgument, "validator address cannot be empty")
9494
}
95+
96+
valAddr, err := sdk.ValAddressFromBech32(req.ValidatorAddr)
97+
if err != nil {
98+
return nil, err
99+
}
95100
ctx := sdk.UnwrapSDKContext(c)
96101

97102
store := ctx.KVStore(k.storeKey)
98-
valStore := prefix.NewStore(store, types.DelegationKey)
99-
delegations, pageRes, err := query.GenericFilteredPaginate(k.cdc, valStore, req.Pagination, func(key []byte, delegation *types.Delegation) (*types.Delegation, error) {
100-
valAddr, err := sdk.ValAddressFromBech32(req.ValidatorAddr)
101-
if err != nil {
102-
return nil, err
103-
}
103+
delStore := prefix.NewStore(store, types.GetDelegationsByValPrefixKey(valAddr))
104104

105-
if !delegation.GetValidatorAddr().Equals(valAddr) {
106-
return nil, nil
105+
var (
106+
dels types.Delegations
107+
pageRes *query.PageResponse
108+
)
109+
pageRes, err = query.Paginate(delStore, req.Pagination, func(delAddr, value []byte) error {
110+
bz := store.Get(types.GetDelegationKey(delAddr, valAddr))
111+
112+
var delegation types.Delegation
113+
err = k.cdc.Unmarshal(bz, &delegation)
114+
if err != nil {
115+
return err
107116
}
108117

109-
return delegation, nil
110-
}, func() *types.Delegation {
111-
return &types.Delegation{}
118+
dels = append(dels, delegation)
119+
return nil
112120
})
113121
if err != nil {
114-
return nil, status.Error(codes.Internal, err.Error())
115-
}
122+
delegations, pageResponse, err := k.getValidatorDelegationsLegacy(ctx, req)
123+
if err != nil {
124+
return nil, status.Error(codes.Internal, err.Error())
125+
}
126+
127+
dels = types.Delegations{}
128+
for _, d := range delegations {
129+
dels = append(dels, *d)
130+
}
116131

117-
dels := types.Delegations{}
118-
for _, d := range delegations {
119-
dels = append(dels, *d)
132+
pageRes = pageResponse
120133
}
121134

122135
delResponses, err := DelegationsToDelegationResponses(ctx, k.Keeper, dels)
@@ -129,6 +142,26 @@ func (k Querier) ValidatorDelegations(c context.Context, req *types.QueryValidat
129142
}, nil
130143
}
131144

145+
func (k Querier) getValidatorDelegationsLegacy(ctx sdk.Context, req *types.QueryValidatorDelegationsRequest) ([]*types.Delegation, *query.PageResponse, error) {
146+
store := ctx.KVStore(k.storeKey)
147+
148+
valStore := prefix.NewStore(store, types.DelegationKey)
149+
return query.GenericFilteredPaginate(k.cdc, valStore, req.Pagination, func(key []byte, delegation *types.Delegation) (*types.Delegation, error) {
150+
valAddr, err := sdk.ValAddressFromBech32(req.ValidatorAddr)
151+
if err != nil {
152+
return nil, err
153+
}
154+
155+
if !delegation.GetValidatorAddr().Equals(valAddr) {
156+
return nil, nil
157+
}
158+
159+
return delegation, nil
160+
}, func() *types.Delegation {
161+
return &types.Delegation{}
162+
})
163+
}
164+
132165
// ValidatorUnbondingDelegations queries unbonding delegations of a validator
133166
func (k Querier) ValidatorUnbondingDelegations(c context.Context, req *types.QueryValidatorUnbondingDelegationsRequest) (*types.QueryValidatorUnbondingDelegationsResponse, error) {
134167
if req == nil {

x/staking/keeper/migrations.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,5 @@ func (m Migrator) Migrate3to4(ctx sdk.Context) error {
4040

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

0 commit comments

Comments
 (0)