mukan-sdk/x/authz/keeper/keeper.go
Mukan Erkin Törük abb1ff956e
Some checks are pending
Build SimApp / build (amd64) (push) Waiting to run
Build SimApp / build (arm64) (push) Waiting to run
CodeQL / Analyze (push) Waiting to run
Build & Push / build (push) Waiting to run
Run Gosec / Gosec (push) Waiting to run
Lint / golangci-lint (push) Waiting to run
Checks dependencies and mocks generation / Check go mod tidy (push) Waiting to run
Checks dependencies and mocks generation / Check up to date mocks (push) Waiting to run
System Tests / setup (push) Waiting to run
System Tests / test-system (push) Blocked by required conditions
System Tests / test-system-legacy (push) Blocked by required conditions
Tests / Code Coverage / split-test-files (push) Waiting to run
Tests / Code Coverage / tests (00) (push) Blocked by required conditions
Tests / Code Coverage / tests (01) (push) Blocked by required conditions
Tests / Code Coverage / tests (02) (push) Blocked by required conditions
Tests / Code Coverage / tests (03) (push) Blocked by required conditions
Tests / Code Coverage / test-integration (push) Waiting to run
Tests / Code Coverage / test-e2e (push) Waiting to run
Tests / Code Coverage / repo-analysis (push) Blocked by required conditions
Tests / Code Coverage / test-sim-nondeterminism (push) Waiting to run
Tests / Code Coverage / test-clientv2 (push) Waiting to run
Tests / Code Coverage / test-core (push) Waiting to run
Tests / Code Coverage / test-depinject (push) Waiting to run
Tests / Code Coverage / test-errors (push) Waiting to run
Tests / Code Coverage / test-math (push) Waiting to run
Tests / Code Coverage / test-schema (push) Waiting to run
Tests / Code Coverage / test-collections (push) Waiting to run
Tests / Code Coverage / test-cosmovisor (push) Waiting to run
Tests / Code Coverage / test-confix (push) Waiting to run
Tests / Code Coverage / test-store (push) Waiting to run
Tests / Code Coverage / test-log (push) Waiting to run
Tests / Code Coverage / test-x-tx (push) Waiting to run
Tests / Code Coverage / test-x-nft (push) Waiting to run
Tests / Code Coverage / test-x-circuit (push) Waiting to run
Tests / Code Coverage / test-x-feegrant (push) Waiting to run
Tests / Code Coverage / test-x-evidence (push) Waiting to run
Tests / Code Coverage / test-x-upgrade (push) Waiting to run
Tests / Code Coverage / test-tools-benchmark (push) Waiting to run
refactor: complete sovereign stack cleanup — all github.com upstream refs purged
2026-05-11 03:46:06 +03:00

450 lines
13 KiB
Go

package keeper
import (
"bytes"
"context"
"fmt"
"strconv"
"time"
abci "git.cw.tr/mukan-network/mukan-consensus/abci/types"
"github.com/cosmos/gogoproto/proto"
corestoretypes "cosmossdk.io/core/store"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/log"
storetypes "cosmossdk.io/store/types"
"git.cw.tr/mukan-network/mukan-sdk/baseapp"
"git.cw.tr/mukan-network/mukan-sdk/codec"
codectypes "git.cw.tr/mukan-network/mukan-sdk/codec/types"
"git.cw.tr/mukan-network/mukan-sdk/runtime"
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
sdkerrors "git.cw.tr/mukan-network/mukan-sdk/types/errors"
"git.cw.tr/mukan-network/mukan-sdk/x/authz"
)
// TODO: Revisit this once we have propoer gas fee framework.
// Tracking issues https://git.cw.tr/mukan-network/mukan-sdk/issues/9054,
// https://git.cw.tr/mukan-network/mukan-sdk/discussions/9072
const gasCostPerIteration = uint64(20)
type Keeper struct {
storeService corestoretypes.KVStoreService
cdc codec.Codec
router baseapp.MessageRouter
authKeeper authz.AccountKeeper
bankKeeper authz.BankKeeper
}
// NewKeeper constructs a message authorization Keeper
func NewKeeper(storeService corestoretypes.KVStoreService, cdc codec.Codec, router baseapp.MessageRouter, ak authz.AccountKeeper) Keeper {
return Keeper{
storeService: storeService,
cdc: cdc,
router: router,
authKeeper: ak,
}
}
// Super ugly hack to not be breaking in v0.50 and v0.47
// DO NOT USE.
func (k Keeper) SetBankKeeper(bk authz.BankKeeper) Keeper {
k.bankKeeper = bk
return k
}
// Logger returns a module-specific logger.
func (k Keeper) Logger(ctx context.Context) log.Logger {
sdkCtx := sdk.UnwrapSDKContext(ctx)
return sdkCtx.Logger().With("module", fmt.Sprintf("x/%s", authz.ModuleName))
}
// getGrant returns grant stored at skey.
func (k Keeper) getGrant(ctx context.Context, skey []byte) (grant authz.Grant, found bool) {
store := k.storeService.OpenKVStore(ctx)
bz, err := store.Get(skey)
if err != nil {
panic(err)
}
if bz == nil {
return grant, false
}
k.cdc.MustUnmarshal(bz, &grant)
return grant, true
}
func (k Keeper) update(ctx context.Context, grantee, granter sdk.AccAddress, updated authz.Authorization) error {
skey := grantStoreKey(grantee, granter, updated.MsgTypeURL())
grant, found := k.getGrant(ctx, skey)
if !found {
return authz.ErrNoAuthorizationFound
}
msg, ok := updated.(proto.Message)
if !ok {
return sdkerrors.ErrPackAny.Wrapf("cannot proto marshal %T", updated)
}
cdcAny, err := codectypes.NewAnyWithValue(msg)
if err != nil {
return err
}
grant.Authorization = cdcAny
store := k.storeService.OpenKVStore(ctx)
return store.Set(skey, k.cdc.MustMarshal(&grant))
}
// DispatchActions attempts to execute the provided messages via authorization
// grants from the message signer to the grantee.
func (k Keeper) DispatchActions(ctx context.Context, grantee sdk.AccAddress, msgs []sdk.Msg) ([][]byte, error) {
results := make([][]byte, len(msgs))
sdkCtx := sdk.UnwrapSDKContext(ctx)
now := sdkCtx.BlockTime()
for i, msg := range msgs {
signers, _, err := k.cdc.GetMsgV1Signers(msg)
if err != nil {
return nil, err
}
if len(signers) != 1 {
return nil, authz.ErrAuthorizationNumOfSigners
}
granter := signers[0]
// If granter != grantee then check authorization.Accept, otherwise we
// implicitly accept.
if !bytes.Equal(granter, grantee) {
skey := grantStoreKey(grantee, granter, sdk.MsgTypeURL(msg))
grant, found := k.getGrant(ctx, skey)
if !found {
return nil, errorsmod.Wrapf(authz.ErrNoAuthorizationFound,
"failed to get grant with given granter: %s, grantee: %s & msgType: %s ", sdk.AccAddress(granter), grantee, sdk.MsgTypeURL(msg))
}
if grant.Expiration != nil && grant.Expiration.Before(now) {
return nil, authz.ErrAuthorizationExpired
}
authorization, err := grant.GetAuthorization()
if err != nil {
return nil, err
}
resp, err := authorization.Accept(sdkCtx, msg)
if err != nil {
return nil, err
}
if resp.Delete {
err = k.DeleteGrant(ctx, grantee, granter, sdk.MsgTypeURL(msg))
} else if resp.Updated != nil {
err = k.update(ctx, grantee, granter, resp.Updated)
}
if err != nil {
return nil, err
}
if !resp.Accept {
return nil, sdkerrors.ErrUnauthorized
}
}
handler := k.router.Handler(msg)
if handler == nil {
return nil, sdkerrors.ErrUnknownRequest.Wrapf("unrecognized message route: %s", sdk.MsgTypeURL(msg))
}
msgResp, err := handler(sdkCtx, msg)
if err != nil {
return nil, errorsmod.Wrapf(err, "failed to execute message; message %v", msg)
}
results[i] = msgResp.Data
// emit the events from the dispatched actions
events := msgResp.Events
sdkEvents := make([]sdk.Event, 0, len(events))
for _, event := range events {
e := event
e.Attributes = append(e.Attributes, abci.EventAttribute{Key: "authz_msg_index", Value: strconv.Itoa(i)})
sdkEvents = append(sdkEvents, sdk.Event(e))
}
sdkCtx.EventManager().EmitEvents(sdkEvents)
}
return results, nil
}
// SaveGrant method grants the provided authorization to the grantee on the granter's account
// with the provided expiration time and insert authorization key into the grants queue. If there is an existing authorization grant for the
// same `sdk.Msg` type, this grant overwrites that.
func (k Keeper) SaveGrant(ctx context.Context, grantee, granter sdk.AccAddress, authorization authz.Authorization, expiration *time.Time) error {
sdkCtx := sdk.UnwrapSDKContext(ctx)
msgType := authorization.MsgTypeURL()
store := k.storeService.OpenKVStore(ctx)
skey := grantStoreKey(grantee, granter, msgType)
grant, err := authz.NewGrant(sdkCtx.BlockTime(), authorization, expiration)
if err != nil {
return err
}
var oldExp *time.Time
if oldGrant, found := k.getGrant(ctx, skey); found {
oldExp = oldGrant.Expiration
}
if oldExp != nil && (expiration == nil || !oldExp.Equal(*expiration)) {
if err = k.removeFromGrantQueue(ctx, skey, granter, grantee, *oldExp); err != nil {
return err
}
}
// If the expiration didn't change, then we don't remove it and we should not insert again
if expiration != nil && (oldExp == nil || !oldExp.Equal(*expiration)) {
if err = k.insertIntoGrantQueue(ctx, granter, grantee, msgType, *expiration); err != nil {
return err
}
}
bz, err := k.cdc.Marshal(&grant)
if err != nil {
return err
}
err = store.Set(skey, bz)
if err != nil {
return err
}
return sdkCtx.EventManager().EmitTypedEvent(&authz.EventGrant{
MsgTypeUrl: authorization.MsgTypeURL(),
Granter: granter.String(),
Grantee: grantee.String(),
})
}
// DeleteGrant revokes any authorization for the provided message type granted to the grantee
// by the granter.
func (k Keeper) DeleteGrant(ctx context.Context, grantee, granter sdk.AccAddress, msgType string) error {
store := k.storeService.OpenKVStore(ctx)
skey := grantStoreKey(grantee, granter, msgType)
grant, found := k.getGrant(ctx, skey)
if !found {
return errorsmod.Wrapf(authz.ErrNoAuthorizationFound, "failed to delete grant with key %s", string(skey))
}
if grant.Expiration != nil {
err := k.removeFromGrantQueue(ctx, skey, granter, grantee, *grant.Expiration)
if err != nil {
return err
}
}
err := store.Delete(skey)
if err != nil {
return err
}
sdkCtx := sdk.UnwrapSDKContext(ctx)
return sdkCtx.EventManager().EmitTypedEvent(&authz.EventRevoke{
MsgTypeUrl: msgType,
Granter: granter.String(),
Grantee: grantee.String(),
})
}
// GetAuthorizations Returns list of `Authorizations` granted to the grantee by the granter.
func (k Keeper) GetAuthorizations(ctx context.Context, grantee, granter sdk.AccAddress) ([]authz.Authorization, error) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
key := grantStoreKey(grantee, granter, "")
iter := storetypes.KVStorePrefixIterator(store, key)
defer iter.Close()
var authorizations []authz.Authorization
for ; iter.Valid(); iter.Next() {
var authorization authz.Grant
if err := k.cdc.Unmarshal(iter.Value(), &authorization); err != nil {
return nil, err
}
a, err := authorization.GetAuthorization()
if err != nil {
return nil, err
}
authorizations = append(authorizations, a)
}
return authorizations, nil
}
// GetAuthorization returns an Authorization and it's expiration time.
// A nil Authorization is returned under the following circumstances:
// - No grant is found.
// - A grant is found, but it is expired.
// - There was an error getting the authorization from the grant.
func (k Keeper) GetAuthorization(ctx context.Context, grantee, granter sdk.AccAddress, msgType string) (authz.Authorization, *time.Time) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
grant, found := k.getGrant(ctx, grantStoreKey(grantee, granter, msgType))
if !found || (grant.Expiration != nil && grant.Expiration.Before(sdkCtx.BlockHeader().Time)) {
return nil, nil
}
auth, err := grant.GetAuthorization()
if err != nil {
return nil, nil
}
return auth, grant.Expiration
}
// IterateGrants iterates over all authorization grants
// This function should be used with caution because it can involve significant IO operations.
// It should not be used in query or msg services without charging additional gas.
// The iteration stops when the handler function returns true or the iterator exhaust.
func (k Keeper) IterateGrants(ctx context.Context,
handler func(granterAddr, granteeAddr sdk.AccAddress, grant authz.Grant) bool,
) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := storetypes.KVStorePrefixIterator(store, GrantKey)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
var grant authz.Grant
granterAddr, granteeAddr, _ := parseGrantStoreKey(iter.Key())
k.cdc.MustUnmarshal(iter.Value(), &grant)
if handler(granterAddr, granteeAddr, grant) {
break
}
}
}
func (k Keeper) getGrantQueueItem(ctx context.Context, expiration time.Time, granter, grantee sdk.AccAddress) (*authz.GrantQueueItem, error) {
store := k.storeService.OpenKVStore(ctx)
bz, err := store.Get(GrantQueueKey(expiration, granter, grantee))
if err != nil {
return nil, err
}
if bz == nil {
return &authz.GrantQueueItem{}, nil
}
var queueItems authz.GrantQueueItem
if err := k.cdc.Unmarshal(bz, &queueItems); err != nil {
return nil, err
}
return &queueItems, nil
}
func (k Keeper) setGrantQueueItem(ctx context.Context, expiration time.Time,
granter, grantee sdk.AccAddress, queueItems *authz.GrantQueueItem,
) error {
store := k.storeService.OpenKVStore(ctx)
bz, err := k.cdc.Marshal(queueItems)
if err != nil {
return err
}
return store.Set(GrantQueueKey(expiration, granter, grantee), bz)
}
// insertIntoGrantQueue inserts a grant key into the grant queue
func (k Keeper) insertIntoGrantQueue(ctx context.Context, granter, grantee sdk.AccAddress, msgType string, expiration time.Time) error {
queueItems, err := k.getGrantQueueItem(ctx, expiration, granter, grantee)
if err != nil {
return err
}
queueItems.MsgTypeUrls = append(queueItems.MsgTypeUrls, msgType)
return k.setGrantQueueItem(ctx, expiration, granter, grantee, queueItems)
}
// removeFromGrantQueue removes a grant key from the grant queue
func (k Keeper) removeFromGrantQueue(ctx context.Context, grantKey []byte, granter, grantee sdk.AccAddress, expiration time.Time) error {
store := k.storeService.OpenKVStore(ctx)
key := GrantQueueKey(expiration, granter, grantee)
bz, err := store.Get(key)
if err != nil {
return err
}
if bz == nil {
return errorsmod.Wrap(authz.ErrNoGrantKeyFound, "can't remove grant from the expire queue, grant key not found")
}
var queueItem authz.GrantQueueItem
if err := k.cdc.Unmarshal(bz, &queueItem); err != nil {
return err
}
_, _, msgType := parseGrantStoreKey(grantKey)
queueItems := queueItem.MsgTypeUrls
sdkCtx := sdk.UnwrapSDKContext(ctx)
for index, typeURL := range queueItems {
sdkCtx.GasMeter().ConsumeGas(gasCostPerIteration, "grant queue")
if typeURL == msgType {
end := len(queueItem.MsgTypeUrls) - 1
queueItems[index] = queueItems[end]
queueItems = queueItems[:end]
if err := k.setGrantQueueItem(ctx, expiration, granter, grantee, &authz.GrantQueueItem{
MsgTypeUrls: queueItems,
}); err != nil {
return err
}
break
}
}
return nil
}
// DequeueAndDeleteExpiredGrants deletes expired grants from the state and grant queue.
func (k Keeper) DequeueAndDeleteExpiredGrants(ctx context.Context) error {
store := k.storeService.OpenKVStore(ctx)
sdkCtx := sdk.UnwrapSDKContext(ctx)
iterator, err := store.Iterator(GrantQueuePrefix, storetypes.InclusiveEndBytes(GrantQueueTimePrefix(sdkCtx.BlockTime())))
if err != nil {
return err
}
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var queueItem authz.GrantQueueItem
if err := k.cdc.Unmarshal(iterator.Value(), &queueItem); err != nil {
return err
}
_, granter, grantee, err := parseGrantQueueKey(iterator.Key())
if err != nil {
return err
}
err = store.Delete(iterator.Key())
if err != nil {
return err
}
for _, typeURL := range queueItem.MsgTypeUrls {
err = store.Delete(grantStoreKey(grantee, granter, typeURL))
if err != nil {
return err
}
}
}
return nil
}