github.com/Finschia/finschia-sdk@v0.49.1/x/authz/keeper/keeper.go (about)

     1  package keeper
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"time"
     7  
     8  	"github.com/Finschia/ostracon/libs/log"
     9  	"github.com/gogo/protobuf/proto"
    10  	abci "github.com/tendermint/tendermint/abci/types"
    11  
    12  	"github.com/Finschia/finschia-sdk/baseapp"
    13  	"github.com/Finschia/finschia-sdk/codec"
    14  	codectypes "github.com/Finschia/finschia-sdk/codec/types"
    15  	sdk "github.com/Finschia/finschia-sdk/types"
    16  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
    17  	"github.com/Finschia/finschia-sdk/x/authz"
    18  )
    19  
    20  type Keeper struct {
    21  	storeKey sdk.StoreKey
    22  	cdc      codec.BinaryCodec
    23  	router   *baseapp.MsgServiceRouter
    24  }
    25  
    26  // NewKeeper constructs a message authorization Keeper
    27  func NewKeeper(storeKey sdk.StoreKey, cdc codec.BinaryCodec, router *baseapp.MsgServiceRouter) Keeper {
    28  	return Keeper{
    29  		storeKey: storeKey,
    30  		cdc:      cdc,
    31  		router:   router,
    32  	}
    33  }
    34  
    35  // Logger returns a module-specific logger.
    36  func (k Keeper) Logger(ctx sdk.Context) log.Logger {
    37  	return ctx.Logger().With("module", fmt.Sprintf("x/%s", authz.ModuleName))
    38  }
    39  
    40  // getGrant returns grant stored at skey.
    41  func (k Keeper) getGrant(ctx sdk.Context, skey []byte) (grant authz.Grant, found bool) {
    42  	store := ctx.KVStore(k.storeKey)
    43  	bz := store.Get(skey)
    44  	if bz == nil {
    45  		return grant, false
    46  	}
    47  	k.cdc.MustUnmarshal(bz, &grant)
    48  	return grant, true
    49  }
    50  
    51  func (k Keeper) update(ctx sdk.Context, grantee, granter sdk.AccAddress, updated authz.Authorization) error {
    52  	skey := grantStoreKey(grantee, granter, updated.MsgTypeURL())
    53  	grant, found := k.getGrant(ctx, skey)
    54  	if !found {
    55  		return sdkerrors.ErrNotFound.Wrap("authorization not found")
    56  	}
    57  
    58  	msg, ok := updated.(proto.Message)
    59  	if !ok {
    60  		return sdkerrors.ErrPackAny.Wrapf("cannot proto marshal %T", updated)
    61  	}
    62  
    63  	any, err := codectypes.NewAnyWithValue(msg)
    64  	if err != nil {
    65  		return err
    66  	}
    67  
    68  	grant.Authorization = any
    69  	store := ctx.KVStore(k.storeKey)
    70  	store.Set(skey, k.cdc.MustMarshal(&grant))
    71  	return nil
    72  }
    73  
    74  // DispatchActions attempts to execute the provided messages via authorization
    75  // grants from the message signer to the grantee.
    76  func (k Keeper) DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, msgs []sdk.Msg) ([][]byte, error) {
    77  	results := make([][]byte, len(msgs))
    78  
    79  	for i, msg := range msgs {
    80  		signers := msg.GetSigners()
    81  		if len(signers) != 1 {
    82  			return nil, sdkerrors.ErrInvalidRequest.Wrap("authorization can be given to msg with only one signer")
    83  		}
    84  
    85  		granter := signers[0]
    86  
    87  		// If granter != grantee then check authorization.Accept, otherwise we
    88  		// implicitly accept.
    89  		if !granter.Equals(grantee) {
    90  			authorization, _ := k.GetCleanAuthorization(ctx, grantee, granter, sdk.MsgTypeURL(msg))
    91  			if authorization == nil {
    92  				return nil, sdkerrors.ErrUnauthorized.Wrap("authorization not found")
    93  			}
    94  			resp, err := authorization.Accept(ctx, msg)
    95  			if err != nil {
    96  				return nil, err
    97  			}
    98  
    99  			if resp.Delete {
   100  				err = k.DeleteGrant(ctx, grantee, granter, sdk.MsgTypeURL(msg))
   101  			} else if resp.Updated != nil {
   102  				err = k.update(ctx, grantee, granter, resp.Updated)
   103  			}
   104  			if err != nil {
   105  				return nil, err
   106  			}
   107  
   108  			if !resp.Accept {
   109  				return nil, sdkerrors.ErrUnauthorized
   110  			}
   111  		}
   112  
   113  		handler := k.router.Handler(msg)
   114  		if handler == nil {
   115  			return nil, sdkerrors.ErrUnknownRequest.Wrapf("unrecognized message route: %s", sdk.MsgTypeURL(msg))
   116  		}
   117  
   118  		msgResp, err := handler(ctx, msg)
   119  		if err != nil {
   120  			return nil, sdkerrors.Wrapf(err, "failed to execute message; message %v", msg)
   121  		}
   122  
   123  		results[i] = msgResp.Data
   124  
   125  		// emit the events from the dispatched actions
   126  		events := msgResp.Events
   127  		sdkEvents := make([]sdk.Event, 0, len(events))
   128  		for _, event := range events {
   129  			e := event
   130  			e.Attributes = append(e.Attributes, abci.EventAttribute{Key: []byte("authz_msg_index"), Value: []byte(strconv.Itoa(i))})
   131  
   132  			sdkEvents = append(sdkEvents, sdk.Event(e))
   133  		}
   134  
   135  		ctx.EventManager().EmitEvents(sdkEvents)
   136  	}
   137  
   138  	return results, nil
   139  }
   140  
   141  // SaveGrant method grants the provided authorization to the grantee on the granter's account
   142  // with the provided expiration time. If there is an existing authorization grant for the
   143  // same `sdk.Msg` type, this grant overwrites that.
   144  func (k Keeper) SaveGrant(ctx sdk.Context, grantee, granter sdk.AccAddress, authorization authz.Authorization, expiration time.Time) error {
   145  	store := ctx.KVStore(k.storeKey)
   146  
   147  	grant, err := authz.NewGrant(ctx.BlockTime(), authorization, expiration)
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	bz := k.cdc.MustMarshal(&grant)
   153  	skey := grantStoreKey(grantee, granter, authorization.MsgTypeURL())
   154  	store.Set(skey, bz)
   155  	return ctx.EventManager().EmitTypedEvent(&authz.EventGrant{
   156  		MsgTypeUrl: authorization.MsgTypeURL(),
   157  		Granter:    granter.String(),
   158  		Grantee:    grantee.String(),
   159  	})
   160  }
   161  
   162  // DeleteGrant revokes any authorization for the provided message type granted to the grantee
   163  // by the granter.
   164  func (k Keeper) DeleteGrant(ctx sdk.Context, grantee, granter sdk.AccAddress, msgType string) error {
   165  	store := ctx.KVStore(k.storeKey)
   166  	skey := grantStoreKey(grantee, granter, msgType)
   167  	_, found := k.getGrant(ctx, skey)
   168  	if !found {
   169  		return sdkerrors.ErrNotFound.Wrap("authorization not found")
   170  	}
   171  	store.Delete(skey)
   172  	return ctx.EventManager().EmitTypedEvent(&authz.EventRevoke{
   173  		MsgTypeUrl: msgType,
   174  		Granter:    granter.String(),
   175  		Grantee:    grantee.String(),
   176  	})
   177  }
   178  
   179  // GetAuthorizations Returns list of `Authorizations` granted to the grantee by the granter.
   180  func (k Keeper) GetAuthorizations(ctx sdk.Context, grantee, granter sdk.AccAddress) (authorizations []authz.Authorization) {
   181  	store := ctx.KVStore(k.storeKey)
   182  	key := grantStoreKey(grantee, granter, "")
   183  	iter := sdk.KVStorePrefixIterator(store, key)
   184  	defer iter.Close()
   185  	var authorization authz.Grant
   186  	for ; iter.Valid(); iter.Next() {
   187  		k.cdc.MustUnmarshal(iter.Value(), &authorization)
   188  		authorizations = append(authorizations, authorization.GetAuthorization())
   189  	}
   190  	return authorizations
   191  }
   192  
   193  // GetCleanAuthorization returns an `Authorization` and it's expiration time for
   194  // (grantee, granter, message name) grant. If there is no grant `nil` is returned.
   195  // If the grant is expired, the grant is revoked, removed from the storage, and `nil` is returned.
   196  func (k Keeper) GetCleanAuthorization(ctx sdk.Context, grantee, granter sdk.AccAddress, msgType string) (cap authz.Authorization, expiration time.Time) {
   197  	grant, found := k.getGrant(ctx, grantStoreKey(grantee, granter, msgType))
   198  	if !found {
   199  		return nil, time.Time{}
   200  	}
   201  	if grant.Expiration.Before(ctx.BlockHeader().Time) {
   202  		_ = k.DeleteGrant(ctx, grantee, granter, msgType)
   203  		return nil, time.Time{}
   204  	}
   205  
   206  	return grant.GetAuthorization(), grant.Expiration
   207  }
   208  
   209  // IterateGrants iterates over all authorization grants
   210  // This function should be used with caution because it can involve significant IO operations.
   211  // It should not be used in query or msg services without charging additional gas.
   212  func (k Keeper) IterateGrants(ctx sdk.Context,
   213  	handler func(granterAddr, granteeAddr sdk.AccAddress, grant authz.Grant) bool,
   214  ) {
   215  	store := ctx.KVStore(k.storeKey)
   216  	iter := sdk.KVStorePrefixIterator(store, GrantKey)
   217  	defer iter.Close()
   218  	for ; iter.Valid(); iter.Next() {
   219  		var grant authz.Grant
   220  		granterAddr, granteeAddr := addressesFromGrantStoreKey(iter.Key())
   221  		k.cdc.MustUnmarshal(iter.Value(), &grant)
   222  		if handler(granterAddr, granteeAddr, grant) {
   223  			break
   224  		}
   225  	}
   226  }
   227  
   228  // ExportGenesis returns a GenesisState for a given context.
   229  func (k Keeper) ExportGenesis(ctx sdk.Context) *authz.GenesisState {
   230  	var entries []authz.GrantAuthorization
   231  	k.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, grant authz.Grant) bool {
   232  		exp := grant.Expiration
   233  		entries = append(entries, authz.GrantAuthorization{
   234  			Granter:       granter.String(),
   235  			Grantee:       grantee.String(),
   236  			Expiration:    exp,
   237  			Authorization: grant.Authorization,
   238  		})
   239  		return false
   240  	})
   241  
   242  	return authz.NewGenesisState(entries)
   243  }
   244  
   245  // InitGenesis new authz genesis
   246  func (k Keeper) InitGenesis(ctx sdk.Context, data *authz.GenesisState) {
   247  	for _, entry := range data.Authorization {
   248  		if entry.Expiration.Before(ctx.BlockTime()) {
   249  			continue
   250  		}
   251  
   252  		grantee := sdk.MustAccAddressFromBech32(entry.Grantee)
   253  		granter := sdk.MustAccAddressFromBech32(entry.Granter)
   254  		a, ok := entry.Authorization.GetCachedValue().(authz.Authorization)
   255  		if !ok {
   256  			panic("expected authorization")
   257  		}
   258  
   259  		err := k.SaveGrant(ctx, grantee, granter, a, entry.Expiration)
   260  		if err != nil {
   261  			panic(err)
   262  		}
   263  	}
   264  }