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

     1  package keeper
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/Finschia/ostracon/libs/log"
     8  
     9  	"github.com/Finschia/finschia-sdk/codec"
    10  	"github.com/Finschia/finschia-sdk/store/prefix"
    11  	sdk "github.com/Finschia/finschia-sdk/types"
    12  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
    13  	"github.com/Finschia/finschia-sdk/x/capability/types"
    14  )
    15  
    16  type (
    17  	// Keeper defines the capability module's keeper. It is responsible for provisioning,
    18  	// tracking, and authenticating capabilities at runtime. During application
    19  	// initialization, the keeper can be hooked up to modules through unique function
    20  	// references so that it can identify the calling module when later invoked.
    21  	//
    22  	// When the initial state is loaded from disk, the keeper allows the ability to
    23  	// create new capability keys for all previously allocated capability identifiers
    24  	// (allocated during execution of past transactions and assigned to particular modes),
    25  	// and keep them in a memory-only store while the chain is running.
    26  	//
    27  	// The keeper allows the ability to create scoped sub-keepers which are tied to
    28  	// a single specific module.
    29  	Keeper struct {
    30  		cdc           codec.BinaryCodec
    31  		storeKey      sdk.StoreKey
    32  		memKey        sdk.StoreKey
    33  		capMap        map[uint64]*types.Capability
    34  		scopedModules map[string]struct{}
    35  		sealed        bool
    36  	}
    37  
    38  	// ScopedKeeper defines a scoped sub-keeper which is tied to a single specific
    39  	// module provisioned by the capability keeper. Scoped keepers must be created
    40  	// at application initialization and passed to modules, which can then use them
    41  	// to claim capabilities they receive and retrieve capabilities which they own
    42  	// by name, in addition to creating new capabilities & authenticating capabilities
    43  	// passed by other modules.
    44  	ScopedKeeper struct {
    45  		cdc      codec.BinaryCodec
    46  		storeKey sdk.StoreKey
    47  		memKey   sdk.StoreKey
    48  		capMap   map[uint64]*types.Capability
    49  		module   string
    50  	}
    51  )
    52  
    53  // NewKeeper constructs a new CapabilityKeeper instance and initializes maps
    54  // for capability map and scopedModules map.
    55  func NewKeeper(cdc codec.BinaryCodec, storeKey, memKey sdk.StoreKey) *Keeper {
    56  	return &Keeper{
    57  		cdc:           cdc,
    58  		storeKey:      storeKey,
    59  		memKey:        memKey,
    60  		capMap:        make(map[uint64]*types.Capability),
    61  		scopedModules: make(map[string]struct{}),
    62  		sealed:        false,
    63  	}
    64  }
    65  
    66  // ScopeToModule attempts to create and return a ScopedKeeper for a given module
    67  // by name. It will panic if the keeper is already sealed or if the module name
    68  // already has a ScopedKeeper.
    69  func (k *Keeper) ScopeToModule(moduleName string) ScopedKeeper {
    70  	if k.sealed {
    71  		panic("cannot scope to module via a sealed capability keeper")
    72  	}
    73  	if strings.TrimSpace(moduleName) == "" {
    74  		panic("cannot scope to an empty module name")
    75  	}
    76  
    77  	if _, ok := k.scopedModules[moduleName]; ok {
    78  		panic(fmt.Sprintf("cannot create multiple scoped keepers for the same module name: %s", moduleName))
    79  	}
    80  
    81  	k.scopedModules[moduleName] = struct{}{}
    82  
    83  	return ScopedKeeper{
    84  		cdc:      k.cdc,
    85  		storeKey: k.storeKey,
    86  		memKey:   k.memKey,
    87  		capMap:   k.capMap,
    88  		module:   moduleName,
    89  	}
    90  }
    91  
    92  // Seal seals the keeper to prevent further modules from creating a scoped keeper.
    93  // Seal may be called during app initialization for applications that do not wish to create scoped keepers dynamically.
    94  func (k *Keeper) Seal() {
    95  	if k.sealed {
    96  		panic("cannot initialize and seal an already sealed capability keeper")
    97  	}
    98  
    99  	k.sealed = true
   100  }
   101  
   102  // InitMemStore will assure that the module store is a memory store (it will panic if it's not)
   103  // and willl initialize it. The function is safe to be called multiple times.
   104  // InitMemStore must be called every time the app starts before the keeper is used (so
   105  // `BeginBlock` or `InitChain` - whichever is first). We need access to the store so we
   106  // can't initialize it in a constructor.
   107  func (k *Keeper) InitMemStore(ctx sdk.Context) {
   108  	memStore := ctx.KVStore(k.memKey)
   109  	memStoreType := memStore.GetStoreType()
   110  	if memStoreType != sdk.StoreTypeMemory {
   111  		panic(fmt.Sprintf("invalid memory store type; got %s, expected: %s", memStoreType, sdk.StoreTypeMemory))
   112  	}
   113  
   114  	// create context with no block gas meter to ensure we do not consume gas during local initialization logic.
   115  	noGasCtx := ctx.WithBlockGasMeter(sdk.NewInfiniteGasMeter())
   116  
   117  	// check if memory store has not been initialized yet by checking if initialized flag is nil.
   118  	if !k.IsInitialized(noGasCtx) {
   119  		prefixStore := prefix.NewStore(noGasCtx.KVStore(k.storeKey), types.KeyPrefixIndexCapability)
   120  		iterator := sdk.KVStorePrefixIterator(prefixStore, nil)
   121  
   122  		// initialize the in-memory store for all persisted capabilities
   123  		defer iterator.Close()
   124  
   125  		for ; iterator.Valid(); iterator.Next() {
   126  			index := types.IndexFromKey(iterator.Key())
   127  
   128  			var capOwners types.CapabilityOwners
   129  
   130  			k.cdc.MustUnmarshal(iterator.Value(), &capOwners)
   131  			k.InitializeCapability(noGasCtx, index, capOwners)
   132  		}
   133  
   134  		// set the initialized flag so we don't rerun initialization logic
   135  		memStore := noGasCtx.KVStore(k.memKey)
   136  		memStore.Set(types.KeyMemInitialized, []byte{1})
   137  	}
   138  }
   139  
   140  // IsInitialized returns true if the keeper is properly initialized, and false otherwise.
   141  func (k *Keeper) IsInitialized(ctx sdk.Context) bool {
   142  	memStore := ctx.KVStore(k.memKey)
   143  	return memStore.Get(types.KeyMemInitialized) != nil
   144  }
   145  
   146  // InitializeIndex sets the index to one (or greater) in InitChain according
   147  // to the GenesisState. It must only be called once.
   148  // It will panic if the provided index is 0, or if the index is already set.
   149  func (k Keeper) InitializeIndex(ctx sdk.Context, index uint64) error {
   150  	if index == 0 {
   151  		panic("SetIndex requires index > 0")
   152  	}
   153  	latest := k.GetLatestIndex(ctx)
   154  	if latest > 0 {
   155  		panic("SetIndex requires index to not be set")
   156  	}
   157  
   158  	// set the global index to the passed index
   159  	store := ctx.KVStore(k.storeKey)
   160  	store.Set(types.KeyIndex, types.IndexToKey(index))
   161  	return nil
   162  }
   163  
   164  // GetLatestIndex returns the latest index of the CapabilityKeeper
   165  func (k Keeper) GetLatestIndex(ctx sdk.Context) uint64 {
   166  	store := ctx.KVStore(k.storeKey)
   167  	return types.IndexFromKey(store.Get(types.KeyIndex))
   168  }
   169  
   170  // SetOwners set the capability owners to the store
   171  func (k Keeper) SetOwners(ctx sdk.Context, index uint64, owners types.CapabilityOwners) {
   172  	prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixIndexCapability)
   173  	indexKey := types.IndexToKey(index)
   174  
   175  	// set owners in persistent store
   176  	prefixStore.Set(indexKey, k.cdc.MustMarshal(&owners))
   177  }
   178  
   179  // GetOwners returns the capability owners with a given index.
   180  func (k Keeper) GetOwners(ctx sdk.Context, index uint64) (types.CapabilityOwners, bool) {
   181  	prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixIndexCapability)
   182  	indexKey := types.IndexToKey(index)
   183  
   184  	// get owners for index from persistent store
   185  	ownerBytes := prefixStore.Get(indexKey)
   186  	if ownerBytes == nil {
   187  		return types.CapabilityOwners{}, false
   188  	}
   189  	var owners types.CapabilityOwners
   190  	k.cdc.MustUnmarshal(ownerBytes, &owners)
   191  	return owners, true
   192  }
   193  
   194  // InitializeCapability takes in an index and an owners array. It creates the capability in memory
   195  // and sets the fwd and reverse keys for each owner in the memstore.
   196  // It is used during initialization from genesis.
   197  func (k Keeper) InitializeCapability(ctx sdk.Context, index uint64, owners types.CapabilityOwners) {
   198  	memStore := ctx.KVStore(k.memKey)
   199  
   200  	cap := types.NewCapability(index)
   201  	for _, owner := range owners.Owners {
   202  		// Set the forward mapping between the module and capability tuple and the
   203  		// capability name in the memKVStore
   204  		memStore.Set(types.FwdCapabilityKey(owner.Module, cap), []byte(owner.Name))
   205  
   206  		// Set the reverse mapping between the module and capability name and the
   207  		// index in the in-memory store. Since marshalling and unmarshalling into a store
   208  		// will change memory address of capability, we simply store index as value here
   209  		// and retrieve the in-memory pointer to the capability from our map
   210  		memStore.Set(types.RevCapabilityKey(owner.Module, owner.Name), sdk.Uint64ToBigEndian(index))
   211  
   212  		// Set the mapping from index from index to in-memory capability in the go map
   213  		k.capMap[index] = cap
   214  	}
   215  }
   216  
   217  // NewCapability attempts to create a new capability with a given name. If the
   218  // capability already exists in the in-memory store, an error will be returned.
   219  // Otherwise, a new capability is created with the current global unique index.
   220  // The newly created capability has the scoped module name and capability name
   221  // tuple set as the initial owner. Finally, the global index is incremented along
   222  // with forward and reverse indexes set in the in-memory store.
   223  //
   224  // Note, namespacing is completely local, which is safe since records are prefixed
   225  // with the module name and no two ScopedKeeper can have the same module name.
   226  func (sk ScopedKeeper) NewCapability(ctx sdk.Context, name string) (*types.Capability, error) {
   227  	if strings.TrimSpace(name) == "" {
   228  		return nil, sdkerrors.Wrap(types.ErrInvalidCapabilityName, "capability name cannot be empty")
   229  	}
   230  	store := ctx.KVStore(sk.storeKey)
   231  
   232  	if _, ok := sk.GetCapability(ctx, name); ok {
   233  		return nil, sdkerrors.Wrapf(types.ErrCapabilityTaken, fmt.Sprintf("module: %s, name: %s", sk.module, name))
   234  	}
   235  
   236  	// create new capability with the current global index
   237  	index := types.IndexFromKey(store.Get(types.KeyIndex))
   238  	cap := types.NewCapability(index)
   239  
   240  	// update capability owner set
   241  	if err := sk.addOwner(ctx, cap, name); err != nil {
   242  		return nil, err
   243  	}
   244  
   245  	// increment global index
   246  	store.Set(types.KeyIndex, types.IndexToKey(index+1))
   247  
   248  	memStore := ctx.KVStore(sk.memKey)
   249  
   250  	// Set the forward mapping between the module and capability tuple and the
   251  	// capability name in the memKVStore
   252  	memStore.Set(types.FwdCapabilityKey(sk.module, cap), []byte(name))
   253  
   254  	// Set the reverse mapping between the module and capability name and the
   255  	// index in the in-memory store. Since marshalling and unmarshalling into a store
   256  	// will change memory address of capability, we simply store index as value here
   257  	// and retrieve the in-memory pointer to the capability from our map
   258  	memStore.Set(types.RevCapabilityKey(sk.module, name), sdk.Uint64ToBigEndian(index))
   259  
   260  	// Set the mapping from index from index to in-memory capability in the go map
   261  	sk.capMap[index] = cap
   262  
   263  	logger(ctx).Info("created new capability", "module", sk.module, "name", name)
   264  
   265  	return cap, nil
   266  }
   267  
   268  // AuthenticateCapability attempts to authenticate a given capability and name
   269  // from a caller. It allows for a caller to check that a capability does in fact
   270  // correspond to a particular name. The scoped keeper will lookup the capability
   271  // from the internal in-memory store and check against the provided name. It returns
   272  // true upon success and false upon failure.
   273  //
   274  // Note, the capability's forward mapping is indexed by a string which should
   275  // contain its unique memory reference.
   276  func (sk ScopedKeeper) AuthenticateCapability(ctx sdk.Context, cap *types.Capability, name string) bool {
   277  	if strings.TrimSpace(name) == "" || cap == nil {
   278  		return false
   279  	}
   280  	return sk.GetCapabilityName(ctx, cap) == name
   281  }
   282  
   283  // ClaimCapability attempts to claim a given Capability. The provided name and
   284  // the scoped module's name tuple are treated as the owner. It will attempt
   285  // to add the owner to the persistent set of capability owners for the capability
   286  // index. If the owner already exists, it will return an error. Otherwise, it will
   287  // also set a forward and reverse index for the capability and capability name.
   288  func (sk ScopedKeeper) ClaimCapability(ctx sdk.Context, cap *types.Capability, name string) error {
   289  	if cap == nil {
   290  		return sdkerrors.Wrap(types.ErrNilCapability, "cannot claim nil capability")
   291  	}
   292  	if strings.TrimSpace(name) == "" {
   293  		return sdkerrors.Wrap(types.ErrInvalidCapabilityName, "capability name cannot be empty")
   294  	}
   295  	// update capability owner set
   296  	if err := sk.addOwner(ctx, cap, name); err != nil {
   297  		return err
   298  	}
   299  
   300  	memStore := ctx.KVStore(sk.memKey)
   301  
   302  	// Set the forward mapping between the module and capability tuple and the
   303  	// capability name in the memKVStore
   304  	memStore.Set(types.FwdCapabilityKey(sk.module, cap), []byte(name))
   305  
   306  	// Set the reverse mapping between the module and capability name and the
   307  	// index in the in-memory store. Since marshalling and unmarshalling into a store
   308  	// will change memory address of capability, we simply store index as value here
   309  	// and retrieve the in-memory pointer to the capability from our map
   310  	memStore.Set(types.RevCapabilityKey(sk.module, name), sdk.Uint64ToBigEndian(cap.GetIndex()))
   311  
   312  	logger(ctx).Info("claimed capability", "module", sk.module, "name", name, "capability", cap.GetIndex())
   313  
   314  	return nil
   315  }
   316  
   317  // ReleaseCapability allows a scoped module to release a capability which it had
   318  // previously claimed or created. After releasing the capability, if no more
   319  // owners exist, the capability will be globally removed.
   320  func (sk ScopedKeeper) ReleaseCapability(ctx sdk.Context, cap *types.Capability) error {
   321  	if cap == nil {
   322  		return sdkerrors.Wrap(types.ErrNilCapability, "cannot release nil capability")
   323  	}
   324  	name := sk.GetCapabilityName(ctx, cap)
   325  	if len(name) == 0 {
   326  		return sdkerrors.Wrap(types.ErrCapabilityNotOwned, sk.module)
   327  	}
   328  
   329  	memStore := ctx.KVStore(sk.memKey)
   330  
   331  	// Delete the forward mapping between the module and capability tuple and the
   332  	// capability name in the memKVStore
   333  	memStore.Delete(types.FwdCapabilityKey(sk.module, cap))
   334  
   335  	// Delete the reverse mapping between the module and capability name and the
   336  	// index in the in-memory store.
   337  	memStore.Delete(types.RevCapabilityKey(sk.module, name))
   338  
   339  	// remove owner
   340  	capOwners := sk.getOwners(ctx, cap)
   341  	capOwners.Remove(types.NewOwner(sk.module, name))
   342  
   343  	prefixStore := prefix.NewStore(ctx.KVStore(sk.storeKey), types.KeyPrefixIndexCapability)
   344  	indexKey := types.IndexToKey(cap.GetIndex())
   345  
   346  	if len(capOwners.Owners) == 0 {
   347  		// remove capability owner set
   348  		prefixStore.Delete(indexKey)
   349  		// since no one owns capability, we can delete capability from map
   350  		delete(sk.capMap, cap.GetIndex())
   351  	} else {
   352  		// update capability owner set
   353  		prefixStore.Set(indexKey, sk.cdc.MustMarshal(capOwners))
   354  	}
   355  
   356  	return nil
   357  }
   358  
   359  // GetCapability allows a module to fetch a capability which it previously claimed
   360  // by name. The module is not allowed to retrieve capabilities which it does not
   361  // own.
   362  func (sk ScopedKeeper) GetCapability(ctx sdk.Context, name string) (*types.Capability, bool) {
   363  	if strings.TrimSpace(name) == "" {
   364  		return nil, false
   365  	}
   366  	memStore := ctx.KVStore(sk.memKey)
   367  
   368  	key := types.RevCapabilityKey(sk.module, name)
   369  	indexBytes := memStore.Get(key)
   370  	index := sdk.BigEndianToUint64(indexBytes)
   371  
   372  	if len(indexBytes) == 0 {
   373  		// If a tx failed and NewCapability got reverted, it is possible
   374  		// to still have the capability in the go map since changes to
   375  		// go map do not automatically get reverted on tx failure,
   376  		// so we delete here to remove unnecessary values in map
   377  		// TODO: Delete index correctly from capMap by storing some reverse lookup
   378  		// in-memory map. Issue: https://github.com/cosmos/cosmos-sdk/issues/7805
   379  
   380  		return nil, false
   381  	}
   382  
   383  	cap := sk.capMap[index]
   384  	if cap == nil {
   385  		panic("capability found in memstore is missing from map")
   386  	}
   387  
   388  	return cap, true
   389  }
   390  
   391  // GetCapabilityName allows a module to retrieve the name under which it stored a given
   392  // capability given the capability
   393  func (sk ScopedKeeper) GetCapabilityName(ctx sdk.Context, cap *types.Capability) string {
   394  	if cap == nil {
   395  		return ""
   396  	}
   397  	memStore := ctx.KVStore(sk.memKey)
   398  
   399  	return string(memStore.Get(types.FwdCapabilityKey(sk.module, cap)))
   400  }
   401  
   402  // GetOwners all the Owners that own the capability associated with the name this ScopedKeeper uses
   403  // to refer to the capability
   404  func (sk ScopedKeeper) GetOwners(ctx sdk.Context, name string) (*types.CapabilityOwners, bool) {
   405  	if strings.TrimSpace(name) == "" {
   406  		return nil, false
   407  	}
   408  	cap, ok := sk.GetCapability(ctx, name)
   409  	if !ok {
   410  		return nil, false
   411  	}
   412  
   413  	prefixStore := prefix.NewStore(ctx.KVStore(sk.storeKey), types.KeyPrefixIndexCapability)
   414  	indexKey := types.IndexToKey(cap.GetIndex())
   415  
   416  	var capOwners types.CapabilityOwners
   417  
   418  	bz := prefixStore.Get(indexKey)
   419  	if len(bz) == 0 {
   420  		return nil, false
   421  	}
   422  
   423  	sk.cdc.MustUnmarshal(bz, &capOwners)
   424  
   425  	return &capOwners, true
   426  }
   427  
   428  // LookupModules returns all the module owners for a given capability
   429  // as a string array and the capability itself.
   430  // The method returns an error if either the capability or the owners cannot be
   431  // retreived from the memstore.
   432  func (sk ScopedKeeper) LookupModules(ctx sdk.Context, name string) ([]string, *types.Capability, error) {
   433  	if strings.TrimSpace(name) == "" {
   434  		return nil, nil, sdkerrors.Wrap(types.ErrInvalidCapabilityName, "cannot lookup modules with empty capability name")
   435  	}
   436  	cap, ok := sk.GetCapability(ctx, name)
   437  	if !ok {
   438  		return nil, nil, sdkerrors.Wrap(types.ErrCapabilityNotFound, name)
   439  	}
   440  
   441  	capOwners, ok := sk.GetOwners(ctx, name)
   442  	if !ok {
   443  		return nil, nil, sdkerrors.Wrap(types.ErrCapabilityOwnersNotFound, name)
   444  	}
   445  
   446  	mods := make([]string, len(capOwners.Owners))
   447  	for i, co := range capOwners.Owners {
   448  		mods[i] = co.Module
   449  	}
   450  
   451  	return mods, cap, nil
   452  }
   453  
   454  func (sk ScopedKeeper) addOwner(ctx sdk.Context, cap *types.Capability, name string) error {
   455  	prefixStore := prefix.NewStore(ctx.KVStore(sk.storeKey), types.KeyPrefixIndexCapability)
   456  	indexKey := types.IndexToKey(cap.GetIndex())
   457  
   458  	capOwners := sk.getOwners(ctx, cap)
   459  
   460  	if err := capOwners.Set(types.NewOwner(sk.module, name)); err != nil {
   461  		return err
   462  	}
   463  
   464  	// update capability owner set
   465  	prefixStore.Set(indexKey, sk.cdc.MustMarshal(capOwners))
   466  
   467  	return nil
   468  }
   469  
   470  func (sk ScopedKeeper) getOwners(ctx sdk.Context, cap *types.Capability) *types.CapabilityOwners {
   471  	prefixStore := prefix.NewStore(ctx.KVStore(sk.storeKey), types.KeyPrefixIndexCapability)
   472  	indexKey := types.IndexToKey(cap.GetIndex())
   473  
   474  	bz := prefixStore.Get(indexKey)
   475  
   476  	if len(bz) == 0 {
   477  		return types.NewCapabilityOwners()
   478  	}
   479  
   480  	var capOwners types.CapabilityOwners
   481  	sk.cdc.MustUnmarshal(bz, &capOwners)
   482  	return &capOwners
   483  }
   484  
   485  func logger(ctx sdk.Context) log.Logger {
   486  	return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
   487  }