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 }