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