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

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