Skip to content

Commit 08c650f

Browse files
committed
!feat: implement secondary index for authz store
Signed-off-by: Artur Troian <troian@users.noreply.github.com>
1 parent ff68ec4 commit 08c650f

File tree

7 files changed

+144
-28
lines changed

7 files changed

+144
-28
lines changed

simapp/go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
module cosmossdk.io/simapp
22

3-
go 1.19
3+
go 1.21
4+
5+
toolchain go1.24.4
46

57
require (
68
cosmossdk.io/api v0.3.1

simapp/sim_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ func TestAppImportExport(t *testing.T) {
203203
stakingtypes.HistoricalInfoKey, stakingtypes.UnbondingIDKey, stakingtypes.UnbondingIndexKey, stakingtypes.UnbondingTypeKey, stakingtypes.ValidatorUpdatesKey,
204204
},
205205
}, // ordering may change but it doesn't matter
206+
206207
{app.GetKey(slashingtypes.StoreKey), newApp.GetKey(slashingtypes.StoreKey), [][]byte{}},
207208
{app.GetKey(minttypes.StoreKey), newApp.GetKey(minttypes.StoreKey), [][]byte{}},
208209
{app.GetKey(distrtypes.StoreKey), newApp.GetKey(distrtypes.StoreKey), [][]byte{}},
@@ -211,7 +212,7 @@ func TestAppImportExport(t *testing.T) {
211212
{app.GetKey(govtypes.StoreKey), newApp.GetKey(govtypes.StoreKey), [][]byte{}},
212213
{app.GetKey(evidencetypes.StoreKey), newApp.GetKey(evidencetypes.StoreKey), [][]byte{}},
213214
{app.GetKey(capabilitytypes.StoreKey), newApp.GetKey(capabilitytypes.StoreKey), [][]byte{}},
214-
{app.GetKey(authzkeeper.StoreKey), newApp.GetKey(authzkeeper.StoreKey), [][]byte{authzkeeper.GrantKey, authzkeeper.GrantQueuePrefix}},
215+
{app.GetKey(authzkeeper.StoreKey), newApp.GetKey(authzkeeper.StoreKey), [][]byte{authzkeeper.GrantKey, authzkeeper.GranteeKey, authzkeeper.GrantQueuePrefix}},
215216
}
216217

217218
for _, skp := range storeKeysPrefixes {

tests/go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
module github.com/cosmos/cosmos-sdk/tests
22

3-
go 1.19
3+
go 1.21
4+
5+
toolchain go1.24.4
46

57
require (
68
cosmossdk.io/api v0.3.1

x/authz/keeper/grpc_query.go

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -146,39 +146,64 @@ func (k Keeper) GranteeGrants(c context.Context, req *authz.QueryGranteeGrantsRe
146146
}
147147

148148
ctx := sdk.UnwrapSDKContext(c)
149-
store := prefix.NewStore(ctx.KVStore(k.storeKey), GrantKey)
150149

151-
authorizations, pageRes, err := query.GenericFilteredPaginate(k.cdc, store, req.Pagination, func(key []byte, auth *authz.Grant) (*authz.GrantAuthorization, error) {
152-
auth1, err := auth.GetAuthorization()
150+
if req.Pagination == nil {
151+
req.Pagination = &query.PageRequest{}
152+
}
153+
154+
if req.Pagination.Limit == 0 {
155+
req.Pagination.Limit = query.DefaultLimit
156+
}
157+
158+
iter := sdk.KVStorePrefixIterator(ctx.KVStore(k.storeKey), granteeStoreKey(grantee, nil))
159+
defer func() {
160+
_ = iter.Close()
161+
}()
162+
163+
var pageRes *query.PageResponse
164+
var grants []*authz.GrantAuthorization
165+
166+
for ; iter.Valid(); iter.Next() {
167+
grantee, granter := parseGranteeStoreKey(iter.Key())
168+
169+
var authorizations []*authz.GrantAuthorization
170+
171+
store := prefix.NewStore(ctx.KVStore(k.storeKey), grantStoreKey(grantee, granter, ""))
172+
authorizations, pageRes, err = query.GenericFilteredPaginate(k.cdc, store, req.Pagination, func(key []byte, auth *authz.Grant) (*authz.GrantAuthorization, error) {
173+
auth1, err := auth.GetAuthorization()
174+
if err != nil {
175+
return nil, err
176+
}
177+
178+
authorizationAny, err := codectypes.NewAnyWithValue(auth1)
179+
if err != nil {
180+
return nil, status.Errorf(codes.Internal, err.Error())
181+
}
182+
183+
return &authz.GrantAuthorization{
184+
Authorization: authorizationAny,
185+
Expiration: auth.Expiration,
186+
Granter: granter.String(),
187+
Grantee: grantee.String(),
188+
}, nil
189+
}, func() *authz.Grant {
190+
return &authz.Grant{}
191+
})
192+
153193
if err != nil {
154194
return nil, err
155195
}
156196

157-
granter, g, _ := parseGrantStoreKey(append(GrantKey, key...))
158-
if !g.Equals(grantee) {
159-
return nil, nil
160-
}
197+
grants = append(grants, authorizations...)
161198

162-
authorizationAny, err := codectypes.NewAnyWithValue(auth1)
163-
if err != nil {
164-
return nil, status.Errorf(codes.Internal, err.Error())
199+
req.Pagination.Limit -= uint64(len(authorizations))
200+
if req.Pagination.Limit == 0 {
201+
break
165202
}
166-
167-
return &authz.GrantAuthorization{
168-
Authorization: authorizationAny,
169-
Expiration: auth.Expiration,
170-
Granter: granter.String(),
171-
Grantee: grantee.String(),
172-
}, nil
173-
}, func() *authz.Grant {
174-
return &authz.Grant{}
175-
})
176-
if err != nil {
177-
return nil, err
178203
}
179204

180205
return &authz.QueryGranteeGrantsResponse{
181-
Grants: authorizations,
206+
Grants: grants,
182207
Pagination: pageRes,
183208
}, nil
184209
}

x/authz/keeper/keeper.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package keeper
22

33
import (
44
"fmt"
5+
"math/big"
56
"strconv"
67
"time"
78

@@ -202,6 +203,8 @@ func (k Keeper) SaveGrant(ctx sdk.Context, grantee, granter sdk.AccAddress, auth
202203
bz := k.cdc.MustMarshal(&grant)
203204
store.Set(skey, bz)
204205

206+
IncGranteeGrants(store, grantee, granter)
207+
205208
return ctx.EventManager().EmitTypedEvent(&authz.EventGrant{
206209
MsgTypeUrl: authorization.MsgTypeURL(),
207210
Granter: granter.String(),
@@ -228,6 +231,8 @@ func (k Keeper) DeleteGrant(ctx sdk.Context, grantee sdk.AccAddress, granter sdk
228231

229232
store.Delete(skey)
230233

234+
decGranteeGrants(store, grantee, granter)
235+
231236
return ctx.EventManager().EmitTypedEvent(&authz.EventRevoke{
232237
MsgTypeUrl: msgType,
233238
Granter: granter.String(),
@@ -240,7 +245,9 @@ func (k Keeper) GetAuthorizations(ctx sdk.Context, grantee sdk.AccAddress, grant
240245
store := ctx.KVStore(k.storeKey)
241246
key := grantStoreKey(grantee, granter, "")
242247
iter := sdk.KVStorePrefixIterator(store, key)
243-
defer iter.Close()
248+
defer func() {
249+
_ = iter.Close()
250+
}()
244251

245252
var authorization authz.Grant
246253
var authorizations []authz.Authorization
@@ -287,10 +294,12 @@ func (k Keeper) IterateGrants(ctx sdk.Context,
287294
handler func(granterAddr sdk.AccAddress, granteeAddr sdk.AccAddress, grant authz.Grant) bool,
288295
) {
289296
store := ctx.KVStore(k.storeKey)
297+
290298
iter := sdk.KVStorePrefixIterator(store, GrantKey)
291299
defer iter.Close()
292300
for ; iter.Valid(); iter.Next() {
293301
var grant authz.Grant
302+
294303
granterAddr, granteeAddr, _ := parseGrantStoreKey(iter.Key())
295304
k.cdc.MustUnmarshal(iter.Value(), &grant)
296305
if handler(granterAddr, granteeAddr, grant) {
@@ -409,3 +418,34 @@ func (k Keeper) DequeueAndDeleteExpiredGrants(ctx sdk.Context) error {
409418

410419
return nil
411420
}
421+
422+
func IncGranteeGrants(store sdk.KVStore, grantee, granter sdk.AccAddress) {
423+
key := granteeStoreKey(grantee, granter)
424+
425+
count := sdk.NewInt(0)
426+
427+
if store.Has(key) {
428+
val := store.Get(key)
429+
bi := new(big.Int).SetBytes(val).Int64()
430+
431+
count = count.AddRaw(bi + 1)
432+
433+
}
434+
435+
store.Set(key, count.BigInt().Bytes())
436+
}
437+
438+
func decGranteeGrants(store sdk.KVStore, grantee, granter sdk.AccAddress) {
439+
skey := granteeStoreKey(grantee, granter)
440+
val := store.Get(skey)
441+
442+
bi := new(big.Int).SetBytes(val).Int64()
443+
bi--
444+
445+
if bi == 0 {
446+
store.Delete(skey)
447+
} else {
448+
count := sdk.NewInt(bi)
449+
store.Set(skey, count.BigInt().Bytes())
450+
}
451+
}

x/authz/keeper/keys.go

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ import (
1515
//
1616
// - 0x01<grant_Bytes>: Grant
1717
// - 0x02<grant_expiration_Bytes>: GrantQueueItem
18+
// - 0x03<grant_Bytes>: Grant by grantee
1819
var (
1920
GrantKey = []byte{0x01} // prefix for each key
2021
GrantQueuePrefix = []byte{0x02}
22+
GranteeKey = []byte{0x03} // prefix for each key
2123
)
2224

2325
var lenTime = len(sdk.FormatTimeBytes(time.Now()))
@@ -38,6 +40,19 @@ func grantStoreKey(grantee sdk.AccAddress, granter sdk.AccAddress, msgType strin
3840
return key
3941
}
4042

43+
// granteeStoreKey - return authorization store key
44+
// Items are stored with the following key: values
45+
//
46+
// - 0x03<granteeAddressLen (1 Byte)><granteeAddress_Bytes><granterAddressLen (1 Byte)><granterAddress_Bytes>
47+
func granteeStoreKey(grantee sdk.AccAddress, granter sdk.AccAddress) []byte {
48+
grantee = address.MustLengthPrefix(grantee)
49+
granter = address.MustLengthPrefix(granter)
50+
51+
key := sdk.AppendLengthPrefixedBytes(GranteeKey, grantee, granter)
52+
53+
return key
54+
}
55+
4156
// parseGrantStoreKey - split granter, grantee address and msg type from the authorization key
4257
func parseGrantStoreKey(key []byte) (granterAddr, granteeAddr sdk.AccAddress, msgType string) {
4358
// key is of format:
@@ -74,6 +89,27 @@ func parseGrantQueueKey(key []byte) (time.Time, sdk.AccAddress, sdk.AccAddress,
7489
return exp, granter, grantee, nil
7590
}
7691

92+
// parseGranteeStoreKey key is of format:
93+
// 0x03<granteeAddressLen (1 Byte)><granteeAddress_Bytes><granterAddressLen (1 Byte)><granterAddress_Bytes>
94+
func parseGranteeStoreKey(key []byte) (sdk.AccAddress, sdk.AccAddress) {
95+
// key is of format:
96+
// 0x03<granteeAddressLen (1 Byte)><granteeAddress_Bytes><granterAddressLen (1 Byte)><granterAddress_Bytes>
97+
kv.AssertKeyAtLeastLength(key, 2)
98+
key = key[1:] // remove prefix key
99+
granteeAddrLen := key[0]
100+
kv.AssertKeyAtLeastLength(key, int(granteeAddrLen))
101+
key = key[1:]
102+
granteeAddr := key[:granteeAddrLen]
103+
kv.AssertKeyAtLeastLength(key, 1)
104+
key = key[granteeAddrLen:]
105+
granterAddrLen := int(key[0])
106+
key = key[1:]
107+
kv.AssertKeyLength(key, granterAddrLen)
108+
granterAddr := key
109+
110+
return granteeAddr, granterAddr
111+
}
112+
77113
// GrantQueueKey - return grant queue store key. If a given grant doesn't have a defined
78114
// expiration, then it should not be used in the pruning queue.
79115
// Key format is:
@@ -95,5 +131,5 @@ func GrantQueueTimePrefix(expiration time.Time) []byte {
95131
// firstAddressFromGrantStoreKey parses the first address only
96132
func firstAddressFromGrantStoreKey(key []byte) sdk.AccAddress {
97133
addrLen := key[0]
98-
return sdk.AccAddress(key[1 : 1+addrLen])
134+
return key[1 : 1+addrLen]
99135
}

x/authz/keeper/keys_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,13 @@ func TestGrantQueueKey(t *testing.T) {
3939
require.Equal(t, granter, granter1)
4040
require.Equal(t, grantee, grantee1)
4141
}
42+
43+
func TestGranteeKey(t *testing.T) {
44+
key := granteeStoreKey(grantee, granter)
45+
46+
require.Len(t, key, len(GranteeKey)+len(address.MustLengthPrefix(grantee))+len(address.MustLengthPrefix(granter)))
47+
48+
grantee1, granter1 := parseGranteeStoreKey(key)
49+
require.Equal(t, granter, granter1)
50+
require.Equal(t, grantee, grantee1)
51+
}

0 commit comments

Comments
 (0)