github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/wasm/keeper/keeper.go (about) 1 package keeper 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/binary" 7 "encoding/json" 8 "fmt" 9 "math" 10 "path/filepath" 11 "strconv" 12 "strings" 13 14 wasmvm "github.com/CosmWasm/wasmvm" 15 wasmvmtypes "github.com/CosmWasm/wasmvm/types" 16 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec" 17 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/prefix" 18 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 19 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 20 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/innertx" 21 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/exported" 22 "github.com/fibonacci-chain/fbc/libs/tendermint/libs/log" 23 paramtypes "github.com/fibonacci-chain/fbc/x/params" 24 "github.com/fibonacci-chain/fbc/x/wasm/ioutils" 25 "github.com/fibonacci-chain/fbc/x/wasm/types" 26 "github.com/fibonacci-chain/fbc/x/wasm/watcher" 27 "github.com/gogo/protobuf/proto" 28 ) 29 30 // contractMemoryLimit is the memory limit of each contract execution (in MiB) 31 // constant value so all nodes run with the same limit. 32 const contractMemoryLimit = 32 33 const SupportedFeatures = "iterator,staking,stargate" 34 35 type contextKey int 36 37 const ( 38 // private type creates an interface key for Context that cannot be accessed by any other package 39 contextKeyQueryStackSize contextKey = iota 40 ) 41 42 // Option is an extension point to instantiate keeper with non default values 43 type Option interface { 44 apply(*Keeper) 45 } 46 47 // WasmVMQueryHandler is an extension point for custom query handler implementations 48 type WasmVMQueryHandler interface { 49 // HandleQuery executes the requested query 50 HandleQuery(ctx sdk.Context, caller sdk.AccAddress, request wasmvmtypes.QueryRequest) ([]byte, error) 51 } 52 53 type CoinTransferrer interface { 54 // TransferCoins sends the coin amounts from the source to the destination with rules applied. 55 TransferCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error 56 } 57 58 // WasmVMResponseHandler is an extension point to handles the response data returned by a contract call. 59 type WasmVMResponseHandler interface { 60 // Handle processes the data returned by a contract invocation. 61 Handle( 62 ctx sdk.Context, 63 contractAddr sdk.AccAddress, 64 ibcPort string, 65 messages []wasmvmtypes.SubMsg, 66 origRspData []byte, 67 ) ([]byte, error) 68 } 69 70 // Keeper will have a reference to Wasmer with it's own data directory. 71 type Keeper struct { 72 storeKey sdk.StoreKey 73 cdc *codec.CodecProxy 74 accountKeeper types.AccountKeeper 75 bank CoinTransferrer 76 portKeeper types.PortKeeper 77 capabilityKeeper types.CapabilityKeeper 78 wasmVM types.WasmerEngine 79 wasmVMQueryHandler WasmVMQueryHandler 80 wasmVMResponseHandler WasmVMResponseHandler 81 messenger Messenger 82 innertxKeeper innertx.InnerTxKeeper 83 84 // queryGasLimit is the max wasmvm gas that can be spent on executing a query with a contract 85 queryGasLimit uint64 86 paramSpace types.Subspace 87 gasRegister GasRegister 88 maxQueryStackSize uint32 89 ada types.DBAdapter 90 } 91 92 type defaultAdapter struct{} 93 94 func (d defaultAdapter) NewStore(_ sdk.GasMeter, store sdk.KVStore, pre []byte) sdk.KVStore { 95 if len(pre) != 0 { 96 store = prefix.NewStore(store, pre) 97 } 98 return watcher.WrapWriteKVStore(store) 99 } 100 101 // NewKeeper creates a new contract Keeper instance 102 // If customEncoders is non-nil, we can use this to override some of the message handler, especially custom 103 func NewKeeper( 104 cdc *codec.CodecProxy, 105 storeKey sdk.StoreKey, 106 paramSpace paramtypes.Subspace, 107 accountKeeper types.AccountKeeper, 108 bankKeeper types.BankKeeper, 109 //distKeeper types.DistributionKeeper, 110 channelKeeper types.ChannelKeeper, 111 portKeeper types.PortKeeper, 112 capabilityKeeper types.CapabilityKeeper, 113 portSource types.ICS20TransferPortSource, 114 router MessageRouter, 115 queryRouter GRPCQueryRouter, 116 homeDir string, 117 wasmConfig types.WasmConfig, 118 supportedFeatures string, 119 opts ...Option, 120 ) Keeper { 121 // set KeyTable if it has not already been set 122 if !paramSpace.HasKeyTable() { 123 paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) 124 } 125 watcher.SetWatchDataManager() 126 k := newKeeper(cdc, storeKey, paramSpace, accountKeeper, bankKeeper, channelKeeper, portKeeper, capabilityKeeper, portSource, router, queryRouter, homeDir, wasmConfig, supportedFeatures, defaultAdapter{}, opts...) 127 accountKeeper.SetObserverKeeper(k) 128 129 return k 130 } 131 132 func NewSimulateKeeper( 133 cdc *codec.CodecProxy, 134 storeKey sdk.StoreKey, 135 paramSpace types.Subspace, 136 accountKeeper types.AccountKeeper, 137 bankKeeper types.BankKeeper, 138 channelKeeper types.ChannelKeeper, 139 portKeeper types.PortKeeper, 140 capabilityKeeper types.CapabilityKeeper, 141 portSource types.ICS20TransferPortSource, 142 router MessageRouter, 143 queryRouter GRPCQueryRouter, 144 homeDir string, 145 wasmConfig types.WasmConfig, 146 supportedFeatures string, 147 opts ...Option, 148 ) Keeper { 149 return newKeeper(cdc, storeKey, paramSpace, accountKeeper, bankKeeper, channelKeeper, portKeeper, capabilityKeeper, portSource, router, queryRouter, homeDir, wasmConfig, supportedFeatures, watcher.Adapter{}, opts...) 150 } 151 152 func newKeeper(cdc *codec.CodecProxy, 153 storeKey sdk.StoreKey, 154 paramSpace types.Subspace, 155 accountKeeper types.AccountKeeper, 156 bankKeeper types.BankKeeper, 157 channelKeeper types.ChannelKeeper, 158 portKeeper types.PortKeeper, 159 capabilityKeeper types.CapabilityKeeper, 160 portSource types.ICS20TransferPortSource, 161 router MessageRouter, 162 queryRouter GRPCQueryRouter, 163 homeDir string, 164 wasmConfig types.WasmConfig, 165 supportedFeatures string, 166 ada types.DBAdapter, 167 opts ...Option, 168 ) Keeper { 169 wasmer, err := wasmvm.NewVM(filepath.Join(homeDir, "wasm"), supportedFeatures, contractMemoryLimit, wasmConfig.ContractDebugMode, wasmConfig.MemoryCacheSize) 170 if err != nil { 171 panic(err) 172 } 173 174 keeper := &Keeper{ 175 storeKey: storeKey, 176 cdc: cdc, 177 wasmVM: wasmer, 178 accountKeeper: accountKeeper, 179 bank: NewBankCoinTransferrer(bankKeeper), 180 portKeeper: portKeeper, 181 capabilityKeeper: capabilityKeeper, 182 messenger: NewDefaultMessageHandler(router, channelKeeper, capabilityKeeper, cdc.GetProtocMarshal(), portSource), 183 queryGasLimit: wasmConfig.SmartQueryGasLimit, 184 paramSpace: paramSpace, 185 gasRegister: NewDefaultWasmGasRegister(), 186 ada: ada, 187 maxQueryStackSize: types.DefaultMaxQueryStackSize, 188 } 189 keeper.wasmVMQueryHandler = DefaultQueryPlugins(bankKeeper, channelKeeper, queryRouter, keeper) 190 for _, o := range opts { 191 o.apply(keeper) 192 } 193 // not updateable, yet 194 keeper.wasmVMResponseHandler = NewDefaultWasmVMContractResponseHandler(NewMessageDispatcher(keeper.messenger, keeper)) 195 return *keeper 196 } 197 198 func (k Keeper) GetStoreKey() sdk.StoreKey { 199 return k.storeKey 200 } 201 202 func (k Keeper) IsContractMethodBlocked(ctx sdk.Context, contractAddr, method string) bool { 203 blockedMethods := k.GetContractMethodBlockedList(ctx, contractAddr) 204 return blockedMethods.IsMethodBlocked(method) 205 } 206 207 func (k Keeper) GetContractMethodBlockedList(ctx sdk.Context, contractAddr string) *types.ContractMethods { 208 return k.getContractMethodBlockedList(ctx, contractAddr) 209 } 210 211 func (k Keeper) getAllBlockedList(ctx sdk.Context) []*types.ContractMethods { 212 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 213 it := sdk.KVStorePrefixIterator(store, types.GetContractMethodBlockedListPrefix("")) 214 defer it.Close() 215 216 var cms []*types.ContractMethods 217 for ; it.Valid(); it.Next() { 218 var method types.ContractMethods 219 err := proto.Unmarshal(it.Value(), &method) 220 if err != nil { 221 panic(err) 222 } 223 cms = append(cms, &method) 224 } 225 return cms 226 } 227 228 func (k Keeper) getContractMethodBlockedList(ctx sdk.Context, contractAddr string) *types.ContractMethods { 229 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 230 key := types.GetContractMethodBlockedListPrefix(contractAddr) 231 data := store.Get(key) 232 var blockedMethods types.ContractMethods 233 err := proto.Unmarshal(data, &blockedMethods) 234 if err != nil { 235 panic(err) 236 } 237 return &blockedMethods 238 } 239 240 func (k Keeper) updateContractMethodBlockedList(ctx sdk.Context, blockedMethods *types.ContractMethods, isDelete bool) error { 241 oldBlockedMethods := k.getContractMethodBlockedList(ctx, blockedMethods.GetContractAddr()) 242 if isDelete { 243 oldBlockedMethods.DeleteMethods(blockedMethods.Methods) 244 } else { 245 oldBlockedMethods.AddMethods(blockedMethods.Methods) 246 } 247 data, err := proto.Marshal(oldBlockedMethods) 248 if err != nil { 249 return err 250 } 251 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 252 key := types.GetContractMethodBlockedListPrefix(blockedMethods.ContractAddr) 253 store.Set(key, data) 254 GetWasmParamsCache().SetNeedBlockedUpdate() 255 return nil 256 } 257 258 func (k Keeper) updateUploadAccessConfig(ctx sdk.Context, config types.AccessConfig) { 259 params := k.GetParams(ctx) 260 params.CodeUploadAccess = config 261 k.SetParams(ctx, params) 262 } 263 264 func (k Keeper) getUploadAccessConfig(ctx sdk.Context) types.AccessConfig { 265 //var a types.AccessConfig 266 //k.paramSpace.Get(ctx, types.ParamStoreKeyUploadAccess, &a) 267 //return a 268 return k.GetParams(ctx).CodeUploadAccess 269 } 270 271 func (k Keeper) getInstantiateAccessConfig(ctx sdk.Context) types.AccessType { 272 //var a types.AccessType 273 //k.paramSpace.Get(ctx, types.ParamStoreKeyInstantiateAccess, &a) 274 //return a 275 return k.GetParams(ctx).InstantiateDefaultPermission 276 } 277 278 // GetParams returns the total set of wasm parameters. 279 func (k Keeper) GetParams(ctx sdk.Context) types.Params { 280 var params types.Params 281 k.paramSpace.GetParamSet(ctx, ¶ms) 282 return params 283 } 284 285 func (k Keeper) SetParams(ctx sdk.Context, ps types.Params) { 286 watcher.SetParams(ps) 287 k.paramSpace.SetParamSet(ctx, &ps) 288 GetWasmParamsCache().SetNeedParamsUpdate() 289 } 290 291 func (k Keeper) OnAccountUpdated(acc exported.Account) { 292 watcher.DeleteAccount(acc.GetAddress()) 293 } 294 295 func (k Keeper) create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte, instantiateAccess *types.AccessConfig, authZ AuthorizationPolicy) (codeID uint64, err error) { 296 if creator == nil { 297 return 0, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "cannot be nil") 298 } 299 300 if !authZ.CanCreateCode(k.getUploadAccessConfig(ctx), creator) { 301 return 0, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not create code") 302 } 303 // figure out proper instantiate access 304 defaultAccessConfig := k.getInstantiateAccessConfig(ctx).With(creator) 305 if instantiateAccess == nil { 306 instantiateAccess = &defaultAccessConfig 307 } else if !instantiateAccess.IsSubset(defaultAccessConfig) { 308 // we enforce this must be subset of default upload access 309 return 0, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "instantiate access must be subset of default upload access") 310 } 311 312 wasmCode, err = ioutils.Uncompress(wasmCode, uint64(types.MaxWasmSize)) 313 if err != nil { 314 return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error()) 315 } 316 ctx.GasMeter().ConsumeGas(k.gasRegister.CompileCosts(len(wasmCode)), "Compiling WASM Bytecode") 317 318 checksum, err := k.wasmVM.Create(wasmCode) 319 if err != nil { 320 return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error()) 321 } 322 report, err := k.wasmVM.AnalyzeCode(checksum) 323 if err != nil { 324 return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error()) 325 } 326 codeID = k.autoIncrementID(ctx, types.KeyLastCodeID) 327 k.Logger(ctx).Debug("storing new contract", "features", report.RequiredFeatures, "code_id", codeID) 328 codeInfo := types.NewCodeInfo(checksum, creator, *instantiateAccess) 329 k.storeCodeInfo(ctx, codeID, codeInfo) 330 331 evt := sdk.NewEvent( 332 types.EventTypeStoreCode, 333 sdk.NewAttribute(types.AttributeKeyCodeID, strconv.FormatUint(codeID, 10)), 334 ) 335 for _, f := range strings.Split(report.RequiredFeatures, ",") { 336 evt.AppendAttributes(sdk.NewAttribute(types.AttributeKeyFeature, strings.TrimSpace(f))) 337 } 338 ctx.EventManager().EmitEvent(evt) 339 340 return codeID, nil 341 } 342 343 func (k Keeper) storeCodeInfo(ctx sdk.Context, codeID uint64, codeInfo types.CodeInfo) { 344 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 345 // 0x01 | codeID (uint64) -> ContractInfo 346 store.Set(types.GetCodeKey(codeID), k.cdc.GetProtocMarshal().MustMarshal(&codeInfo)) 347 } 348 349 func (k Keeper) importCode(ctx sdk.Context, codeID uint64, codeInfo types.CodeInfo, wasmCode []byte) error { 350 wasmCode, err := ioutils.Uncompress(wasmCode, uint64(types.MaxWasmSize)) 351 if err != nil { 352 return sdkerrors.Wrap(types.ErrCreateFailed, err.Error()) 353 } 354 newCodeHash, err := k.wasmVM.Create(wasmCode) 355 if err != nil { 356 return sdkerrors.Wrap(types.ErrCreateFailed, err.Error()) 357 } 358 if !bytes.Equal(codeInfo.CodeHash, newCodeHash) { 359 return sdkerrors.Wrap(types.ErrInvalid, "code hashes not same") 360 } 361 362 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 363 364 key := types.GetCodeKey(codeID) 365 if store.Has(key) { 366 return sdkerrors.Wrapf(types.ErrDuplicate, "duplicate code: %d", codeID) 367 } 368 // 0x01 | codeID (uint64) -> ContractInfo 369 store.Set(key, k.cdc.GetProtocMarshal().MustMarshal(&codeInfo)) 370 return nil 371 } 372 373 func (k Keeper) instantiate(ctx sdk.Context, codeID uint64, creator, admin sdk.AccAddress, initMsg []byte, label string, deposit sdk.Coins, authZ AuthorizationPolicy) (sdk.AccAddress, []byte, error) { 374 //defer telemetry.MeasureSince(time.Now(), "wasm", "contract", "instantiate") 375 instanceCosts := k.gasRegister.NewContractInstanceCosts(k.IsPinnedCode(ctx, codeID), len(initMsg)) 376 ctx.GasMeter().ConsumeGas(instanceCosts, "Loading CosmWasm module: instantiate") 377 378 // create contract address 379 contractAddress := k.generateContractAddress(ctx, codeID) 380 existingAcct := k.accountKeeper.GetAccount(ctx, contractAddress) 381 if existingAcct != nil { 382 return nil, nil, sdkerrors.Wrap(types.ErrAccountExists, existingAcct.GetAddress().String()) 383 } 384 385 // deposit initial contract funds 386 if !deposit.IsZero() { 387 if err := k.bank.TransferCoins(ctx, creator, contractAddress, deposit); err != nil { 388 return nil, nil, err 389 } 390 } else { 391 // create an empty account (so we don't have issues later) 392 // TODO: can we remove this? 393 contractAccount := k.accountKeeper.NewAccountWithAddress(ctx, contractAddress) 394 k.accountKeeper.SetAccount(ctx, contractAccount) 395 } 396 397 // get contact info 398 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 399 codeInfo := k.GetCodeInfo(ctx, codeID) 400 if codeInfo == nil { 401 return nil, nil, sdkerrors.Wrap(types.ErrNotFound, "code") 402 } 403 404 if !authZ.CanInstantiateContract(codeInfo.InstantiateConfig, creator) { 405 return nil, nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not instantiate") 406 } 407 408 // prepare params for contract instantiate call 409 env := types.NewEnv(ctx, contractAddress) 410 adapters := sdk.CoinsToCoinAdapters(deposit) 411 info := types.NewInfo(creator, adapters) 412 413 // create prefixed data store 414 // 0x03 | BuildContractAddress (sdk.AccAddress) 415 prefixStore := prefix.NewStore(store, types.GetContractStorePrefix(contractAddress)) 416 prefixStoreAdapter := types.NewStoreAdapter(prefixStore) 417 418 // prepare querier 419 querier := k.newQueryHandler(ctx, contractAddress) 420 421 // instantiate wasm contract 422 gas := k.runtimeGasForContract(ctx) 423 res, gasUsed, err := k.wasmVM.Instantiate(codeInfo.CodeHash, env, info, initMsg, prefixStoreAdapter, cosmwasmAPI, querier, k.gasMeter(ctx), gas, costJSONDeserialization) 424 k.consumeRuntimeGas(ctx, gasUsed) 425 if !ctx.IsCheckTx() && k.innertxKeeper != nil { 426 k.innertxKeeper.UpdateWasmInnerTx(ctx.TxBytes(), ctx.BlockHeight(), innertx.CosmosDepth, creator, contractAddress, innertx.CosmosCallType, types.InstantiateInnertxName, sdk.Coins{}, err, k.gasRegister.FromWasmVMGas(gasUsed), string(initMsg)) 427 } 428 if err != nil { 429 return nil, nil, sdkerrors.Wrap(types.ErrInstantiateFailed, err.Error()) 430 } 431 432 // persist instance first 433 createdAt := types.NewAbsoluteTxPosition(ctx) 434 contractInfo := types.NewContractInfo(codeID, creator, admin, label, createdAt) 435 436 // check for IBC flag 437 report, err := k.wasmVM.AnalyzeCode(codeInfo.CodeHash) 438 if err != nil { 439 return nil, nil, sdkerrors.Wrap(types.ErrInstantiateFailed, err.Error()) 440 } 441 if report.HasIBCEntryPoints { 442 // register IBC port 443 ibcPort, err := k.ensureIbcPort(ctx, contractAddress) 444 if err != nil { 445 return nil, nil, err 446 } 447 contractInfo.IBCPortID = ibcPort 448 } 449 450 // store contract before dispatch so that contract could be called back 451 historyEntry := contractInfo.InitialHistory(initMsg) 452 k.addToContractCodeSecondaryIndex(ctx, contractAddress, historyEntry) 453 k.appendToContractHistory(ctx, contractAddress, historyEntry) 454 k.storeContractInfo(ctx, contractAddress, &contractInfo) 455 456 ctx.EventManager().EmitEvent(sdk.NewEvent( 457 types.EventTypeInstantiate, 458 sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()), 459 sdk.NewAttribute(types.AttributeKeyCodeID, strconv.FormatUint(codeID, 10)), 460 )) 461 462 data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res.Messages, res.Attributes, res.Data, res.Events) 463 if err != nil { 464 return nil, nil, sdkerrors.Wrap(err, "dispatch") 465 } 466 467 return contractAddress, data, nil 468 } 469 470 // Execute executes the contract instance 471 func (k Keeper) execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress, msg []byte, coins sdk.Coins) ([]byte, error) { 472 //defer telemetry.MeasureSince(time.Now(), "wasm", "contract", "execute") 473 contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddress) 474 if err != nil { 475 return nil, err 476 } 477 478 executeCosts := k.gasRegister.InstantiateContractCosts(k.IsPinnedCode(ctx, contractInfo.CodeID), len(msg)) 479 ctx.GasMeter().ConsumeGas(executeCosts, "Loading CosmWasm module: execute") 480 481 // add more funds 482 if !coins.IsZero() { 483 if err := k.bank.TransferCoins(ctx, caller, contractAddress, coins); err != nil { 484 return nil, err 485 } 486 } 487 488 env := types.NewEnv(ctx, contractAddress) 489 adapters := sdk.CoinsToCoinAdapters(coins) 490 info := types.NewInfo(caller, adapters) 491 492 // prepare querier 493 querier := k.newQueryHandler(ctx, contractAddress) 494 gas := k.runtimeGasForContract(ctx) 495 if k.GetParams(ctx).UseContractBlockedList { 496 var methodsMap map[string]interface{} 497 err = json.Unmarshal(msg, &methodsMap) 498 if err != nil { 499 return nil, err 500 } 501 for method := range methodsMap { 502 if k.IsContractMethodBlocked(ctx, contractAddress.String(), method) { 503 return nil, sdkerrors.Wrap(types.ErrExecuteFailed, fmt.Sprintf("%s method of contract %s is not allowed", contractAddress.String(), method)) 504 } 505 } 506 } 507 508 res, gasUsed, execErr := k.wasmVM.Execute(codeInfo.CodeHash, env, info, msg, prefixStore, cosmwasmAPI, querier, k.gasMeter(ctx), gas, costJSONDeserialization) 509 k.consumeRuntimeGas(ctx, gasUsed) 510 if !ctx.IsCheckTx() && k.innertxKeeper != nil { 511 k.innertxKeeper.UpdateWasmInnerTx(ctx.TxBytes(), ctx.BlockHeight(), innertx.CosmosDepth, caller, contractAddress, innertx.CosmosCallType, types.ExecuteInnertxName, coins, err, k.gasRegister.FromWasmVMGas(gasUsed), string(msg)) 512 } 513 if execErr != nil { 514 return nil, sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) 515 } 516 517 ctx.EventManager().EmitEvent(sdk.NewEvent( 518 types.EventTypeExecute, 519 sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()), 520 )) 521 522 data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res.Messages, res.Attributes, res.Data, res.Events) 523 if err != nil { 524 return nil, sdkerrors.Wrap(err, "dispatch") 525 } 526 527 return data, nil 528 } 529 530 func (k Keeper) migrate(ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress, newCodeID uint64, msg []byte, authZ AuthorizationPolicy) ([]byte, error) { 531 //defer telemetry.MeasureSince(time.Now(), "wasm", "contract", "migrate") 532 migrateSetupCosts := k.gasRegister.InstantiateContractCosts(k.IsPinnedCode(ctx, newCodeID), len(msg)) 533 ctx.GasMeter().ConsumeGas(migrateSetupCosts, "Loading CosmWasm module: migrate") 534 535 contractInfo := k.GetContractInfo(ctx, contractAddress) 536 if contractInfo == nil { 537 return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "unknown contract") 538 } 539 if !authZ.CanModifyContract(contractInfo.AdminAddr(), caller) { 540 return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not migrate") 541 } 542 543 newCodeInfo := k.GetCodeInfo(ctx, newCodeID) 544 if newCodeInfo == nil { 545 return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "unknown code") 546 } 547 548 // check for IBC flag 549 switch report, err := k.wasmVM.AnalyzeCode(newCodeInfo.CodeHash); { 550 case err != nil: 551 return nil, sdkerrors.Wrap(types.ErrMigrationFailed, err.Error()) 552 case !report.HasIBCEntryPoints && contractInfo.IBCPortID != "": 553 // prevent update to non ibc contract 554 return nil, sdkerrors.Wrap(types.ErrMigrationFailed, "requires ibc callbacks") 555 case report.HasIBCEntryPoints && contractInfo.IBCPortID == "": 556 // add ibc port 557 ibcPort, err := k.ensureIbcPort(ctx, contractAddress) 558 if err != nil { 559 return nil, err 560 } 561 contractInfo.IBCPortID = ibcPort 562 } 563 564 env := types.NewEnv(ctx, contractAddress) 565 566 // prepare querier 567 querier := k.newQueryHandler(ctx, contractAddress) 568 569 prefixStoreKey := types.GetContractStorePrefix(contractAddress) 570 prefixStore := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), prefixStoreKey) 571 prefixAdapater := types.NewStoreAdapter(prefixStore) 572 573 gas := k.runtimeGasForContract(ctx) 574 res, gasUsed, err := k.wasmVM.Migrate(newCodeInfo.CodeHash, env, msg, &prefixAdapater, cosmwasmAPI, &querier, k.gasMeter(ctx), gas, costJSONDeserialization) 575 k.consumeRuntimeGas(ctx, gasUsed) 576 if !ctx.IsCheckTx() && k.innertxKeeper != nil { 577 k.innertxKeeper.UpdateWasmInnerTx(ctx.TxBytes(), ctx.BlockHeight(), innertx.CosmosDepth, caller, contractAddress, innertx.CosmosCallType, types.MigrateInnertxName, sdk.Coins{}, err, k.gasRegister.FromWasmVMGas(gasUsed), string(msg)) 578 } 579 if err != nil { 580 return nil, sdkerrors.Wrap(types.ErrMigrationFailed, err.Error()) 581 } 582 583 // delete old secondary index entry 584 k.removeFromContractCodeSecondaryIndex(ctx, contractAddress, k.getLastContractHistoryEntry(ctx, contractAddress)) 585 // persist migration updates 586 historyEntry := contractInfo.AddMigration(ctx, newCodeID, msg) 587 k.appendToContractHistory(ctx, contractAddress, historyEntry) 588 k.addToContractCodeSecondaryIndex(ctx, contractAddress, historyEntry) 589 k.storeContractInfo(ctx, contractAddress, contractInfo) 590 591 ctx.EventManager().EmitEvent(sdk.NewEvent( 592 types.EventTypeMigrate, 593 sdk.NewAttribute(types.AttributeKeyCodeID, strconv.FormatUint(newCodeID, 10)), 594 sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()), 595 )) 596 597 data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res.Messages, res.Attributes, res.Data, res.Events) 598 if err != nil { 599 return nil, sdkerrors.Wrap(err, "dispatch") 600 } 601 602 return data, nil 603 } 604 605 // Sudo allows priviledged access to a contract. This can never be called by an external tx, but only by 606 // another native Go module directly, or on-chain governance (if sudo proposals are enabled). Thus, the keeper doesn't 607 // place any access controls on it, that is the responsibility or the app developer (who passes the wasm.Keeper in app.go) 608 func (k Keeper) Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error) { 609 //defer telemetry.MeasureSince(time.Now(), "wasm", "contract", "sudo") 610 contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddress) 611 if err != nil { 612 return nil, err 613 } 614 615 sudoSetupCosts := k.gasRegister.InstantiateContractCosts(k.IsPinnedCode(ctx, contractInfo.CodeID), len(msg)) 616 ctx.GasMeter().ConsumeGas(sudoSetupCosts, "Loading CosmWasm module: sudo") 617 618 env := types.NewEnv(ctx, contractAddress) 619 620 // prepare querier 621 querier := k.newQueryHandler(ctx, contractAddress) 622 gas := k.runtimeGasForContract(ctx) 623 res, gasUsed, execErr := k.wasmVM.Sudo(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, k.gasMeter(ctx), gas, costJSONDeserialization) 624 k.consumeRuntimeGas(ctx, gasUsed) 625 if execErr != nil { 626 return nil, sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) 627 } 628 629 ctx.EventManager().EmitEvent(sdk.NewEvent( 630 types.EventTypeSudo, 631 sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()), 632 )) 633 634 data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res.Messages, res.Attributes, res.Data, res.Events) 635 if err != nil { 636 return nil, sdkerrors.Wrap(err, "dispatch") 637 } 638 639 return data, nil 640 } 641 642 // reply is only called from keeper internal functions (dispatchSubmessages) after processing the submessage 643 func (k Keeper) reply(ctx sdk.Context, contractAddress sdk.AccAddress, reply wasmvmtypes.Reply) ([]byte, error) { 644 contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddress) 645 if err != nil { 646 return nil, err 647 } 648 649 // always consider this pinned 650 replyCosts := k.gasRegister.ReplyCosts(true, reply) 651 ctx.GasMeter().ConsumeGas(replyCosts, "Loading CosmWasm module: reply") 652 653 env := types.NewEnv(ctx, contractAddress) 654 655 // prepare querier 656 querier := k.newQueryHandler(ctx, contractAddress) 657 gas := k.runtimeGasForContract(ctx) 658 res, gasUsed, execErr := k.wasmVM.Reply(codeInfo.CodeHash, env, reply, prefixStore, cosmwasmAPI, querier, k.gasMeter(ctx), gas, costJSONDeserialization) 659 k.consumeRuntimeGas(ctx, gasUsed) 660 if execErr != nil { 661 return nil, sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) 662 } 663 664 ctx.EventManager().EmitEvent(sdk.NewEvent( 665 types.EventTypeReply, 666 sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()), 667 )) 668 669 data, err := k.handleContractResponse(ctx, contractAddress, contractInfo.IBCPortID, res.Messages, res.Attributes, res.Data, res.Events) 670 if err != nil { 671 return nil, sdkerrors.Wrap(err, "dispatch") 672 } 673 674 return data, nil 675 } 676 677 // addToContractCodeSecondaryIndex adds element to the index for contracts-by-codeid queries 678 func (k Keeper) addToContractCodeSecondaryIndex(ctx sdk.Context, contractAddress sdk.AccAddress, entry types.ContractCodeHistoryEntry) { 679 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 680 store.Set(types.GetContractByCreatedSecondaryIndexKey(contractAddress, entry), []byte{}) 681 } 682 683 // removeFromContractCodeSecondaryIndex removes element to the index for contracts-by-codeid queries 684 func (k Keeper) removeFromContractCodeSecondaryIndex(ctx sdk.Context, contractAddress sdk.AccAddress, entry types.ContractCodeHistoryEntry) { 685 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 686 store.Delete(types.GetContractByCreatedSecondaryIndexKey(contractAddress, entry)) 687 } 688 689 // IterateContractsByCode iterates over all contracts with given codeID ASC on code update time. 690 func (k Keeper) IterateContractsByCode(ctx sdk.Context, codeID uint64, cb func(address sdk.AccAddress) bool) { 691 prefixStore := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), types.GetContractByCodeIDSecondaryIndexPrefix(codeID)) 692 iter := prefixStore.Iterator(nil, nil) 693 defer iter.Close() 694 695 for ; iter.Valid(); iter.Next() { 696 key := iter.Key() 697 if cb(key[types.AbsoluteTxPositionLen:]) { 698 return 699 } 700 } 701 } 702 703 func (k Keeper) setContractAdmin(ctx sdk.Context, contractAddress, caller, newAdmin sdk.AccAddress, authZ AuthorizationPolicy) (err error) { 704 gas := ctx.GasMeter().GasConsumed() 705 defer func() { 706 if !ctx.IsCheckTx() && k.innertxKeeper != nil { 707 k.innertxKeeper.UpdateWasmInnerTx(ctx.TxBytes(), ctx.BlockHeight(), innertx.CosmosDepth, caller, contractAddress, innertx.CosmosCallType, types.SetContractAdminInnertxName, sdk.Coins{}, err, ctx.GasMeter().GasConsumed()-gas, "") 708 } 709 }() 710 contractInfo := k.GetContractInfo(ctx, contractAddress) 711 if contractInfo == nil { 712 return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "unknown contract") 713 } 714 if !authZ.CanModifyContract(contractInfo.AdminAddr(), caller) { 715 return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not modify contract") 716 } 717 contractInfo.Admin = newAdmin.String() 718 k.storeContractInfo(ctx, contractAddress, contractInfo) 719 return nil 720 } 721 722 func (k Keeper) appendToContractHistory(ctx sdk.Context, contractAddr sdk.AccAddress, newEntries ...types.ContractCodeHistoryEntry) { 723 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 724 // find last element position 725 var pos uint64 726 prefixStore := prefix.NewStore(store, types.GetContractCodeHistoryElementPrefix(contractAddr)) 727 iter := prefixStore.ReverseIterator(nil, nil) 728 defer iter.Close() 729 730 if iter.Valid() { 731 pos = sdk.BigEndianToUint64(iter.Key()) 732 } 733 // then store with incrementing position 734 for _, e := range newEntries { 735 pos++ 736 key := types.GetContractCodeHistoryElementKey(contractAddr, pos) 737 store.Set(key, k.cdc.GetProtocMarshal().MustMarshal(&e)) //nolint:gosec 738 } 739 } 740 741 func (k Keeper) GetContractHistory(ctx sdk.Context, contractAddr sdk.AccAddress) []types.ContractCodeHistoryEntry { 742 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), types.GetContractCodeHistoryElementPrefix(contractAddr)) 743 744 r := make([]types.ContractCodeHistoryEntry, 0) 745 iter := store.Iterator(nil, nil) 746 defer iter.Close() 747 748 for ; iter.Valid(); iter.Next() { 749 var e types.ContractCodeHistoryEntry 750 k.cdc.GetProtocMarshal().MustUnmarshal(iter.Value(), &e) 751 r = append(r, e) 752 } 753 return r 754 } 755 756 // getLastContractHistoryEntry returns the last element from history. To be used internally only as it panics when none exists 757 func (k Keeper) getLastContractHistoryEntry(ctx sdk.Context, contractAddr sdk.AccAddress) types.ContractCodeHistoryEntry { 758 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), types.GetContractCodeHistoryElementPrefix(contractAddr)) 759 iter := store.ReverseIterator(nil, nil) 760 defer iter.Close() 761 762 var r types.ContractCodeHistoryEntry 763 if !iter.Valid() { 764 // all contracts have a history 765 panic(fmt.Sprintf("no history for %s", contractAddr.String())) 766 } 767 k.cdc.GetProtocMarshal().MustUnmarshal(iter.Value(), &r) 768 return r 769 } 770 771 // QuerySmart queries the smart contract itself. 772 func (k Keeper) QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { 773 //defer telemetry.MeasureSince(time.Now(), "wasm", "contract", "query-smart") 774 775 // checks and increase query stack size 776 ctx, err := checkAndIncreaseQueryStackSize(ctx, k.maxQueryStackSize) 777 if err != nil { 778 return nil, err 779 } 780 781 contractInfo, codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr) 782 if err != nil { 783 return nil, err 784 } 785 786 smartQuerySetupCosts := k.gasRegister.InstantiateContractCosts(k.IsPinnedCode(ctx, contractInfo.CodeID), len(req)) 787 ctx.GasMeter().ConsumeGas(smartQuerySetupCosts, "Loading CosmWasm module: query") 788 789 // prepare querier 790 querier := k.newQueryHandler(ctx, contractAddr) 791 792 env := types.NewEnv(ctx, contractAddr) 793 queryResult, gasUsed, qErr := k.wasmVM.Query(codeInfo.CodeHash, env, req, prefixStore, cosmwasmAPI, querier, k.gasMeter(ctx), k.runtimeGasForContract(ctx), costJSONDeserialization) 794 k.consumeRuntimeGas(ctx, gasUsed) 795 if qErr != nil { 796 return nil, sdkerrors.Wrap(types.ErrQueryFailed, qErr.Error()) 797 } 798 return queryResult, nil 799 } 800 801 func checkAndIncreaseQueryStackSize(ctx sdk.Context, maxQueryStackSize uint32) (sdk.Context, error) { 802 var queryStackSize uint32 803 804 // read current value 805 if size := ctx.Context().Value(contextKeyQueryStackSize); size != nil { 806 queryStackSize = size.(uint32) 807 } else { 808 queryStackSize = 0 809 } 810 811 // increase 812 queryStackSize++ 813 814 // did we go too far? 815 if queryStackSize > maxQueryStackSize { 816 return ctx, types.ErrExceedMaxQueryStackSize 817 } 818 819 // set updated stack size 820 contextCtx := context.WithValue(ctx.Context(), contextKeyQueryStackSize, queryStackSize) 821 ctx.SetContext(contextCtx) 822 823 return ctx, nil 824 } 825 826 // QueryRaw returns the contract's state for give key. Returns `nil` when key is `nil`. 827 func (k Keeper) QueryRaw(ctx sdk.Context, contractAddress sdk.AccAddress, key []byte) []byte { 828 //defer telemetry.MeasureSince(time.Now(), "wasm", "contract", "query-raw") 829 if key == nil { 830 return nil 831 } 832 prefixStoreKey := types.GetContractStorePrefix(contractAddress) 833 prefixStore := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), prefixStoreKey) 834 return prefixStore.Get(key) 835 } 836 837 func (k Keeper) contractInstance(ctx sdk.Context, contractAddress sdk.AccAddress) (types.ContractInfo, types.CodeInfo, types.StoreAdapter, error) { 838 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 839 contractBz := store.Get(types.GetContractAddressKey(contractAddress)) 840 if contractBz == nil { 841 return types.ContractInfo{}, types.CodeInfo{}, types.StoreAdapter{}, sdkerrors.Wrap(types.ErrNotFound, "contract") 842 } 843 var contractInfo types.ContractInfo 844 k.cdc.GetProtocMarshal().MustUnmarshal(contractBz, &contractInfo) 845 846 codeInfoBz := store.Get(types.GetCodeKey(contractInfo.CodeID)) 847 if codeInfoBz == nil { 848 return contractInfo, types.CodeInfo{}, types.StoreAdapter{}, sdkerrors.Wrap(types.ErrNotFound, "code info") 849 } 850 var codeInfo types.CodeInfo 851 k.cdc.GetProtocMarshal().MustUnmarshal(codeInfoBz, &codeInfo) 852 prefixStoreKey := types.GetContractStorePrefix(contractAddress) 853 prefixStore := prefix.NewStore(store, prefixStoreKey) 854 return contractInfo, codeInfo, types.NewStoreAdapter(prefixStore), nil 855 } 856 857 func (k Keeper) GetContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo { 858 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 859 var contract types.ContractInfo 860 contractBz := store.Get(types.GetContractAddressKey(contractAddress)) 861 if contractBz == nil { 862 return nil 863 } 864 k.cdc.GetProtocMarshal().MustUnmarshal(contractBz, &contract) 865 return &contract 866 } 867 868 func (k Keeper) HasContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) bool { 869 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 870 return store.Has(types.GetContractAddressKey(contractAddress)) 871 } 872 873 // storeContractInfo persists the ContractInfo. No secondary index updated here. 874 func (k Keeper) storeContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress, contract *types.ContractInfo) { 875 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 876 store.Set(types.GetContractAddressKey(contractAddress), k.cdc.GetProtocMarshal().MustMarshal(contract)) 877 } 878 879 func (k Keeper) IterateContractInfo(ctx sdk.Context, cb func(sdk.AccAddress, types.ContractInfo) bool) { 880 prefixStore := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), types.ContractKeyPrefix) 881 iter := prefixStore.Iterator(nil, nil) 882 defer iter.Close() 883 884 for ; iter.Valid(); iter.Next() { 885 var contract types.ContractInfo 886 k.cdc.GetProtocMarshal().MustUnmarshal(iter.Value(), &contract) 887 // cb returns true to stop early 888 if cb(iter.Key(), contract) { 889 break 890 } 891 } 892 } 893 894 // IterateContractState iterates through all elements of the key value store for the given contract address and passes 895 // them to the provided callback function. The callback method can return true to abort early. 896 func (k Keeper) IterateContractState(ctx sdk.Context, contractAddress sdk.AccAddress, cb func(key, value []byte) bool) { 897 prefixStoreKey := types.GetContractStorePrefix(contractAddress) 898 prefixStore := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), prefixStoreKey) 899 900 iter := prefixStore.Iterator(nil, nil) 901 defer iter.Close() 902 903 for ; iter.Valid(); iter.Next() { 904 if cb(iter.Key(), iter.Value()) { 905 break 906 } 907 } 908 } 909 910 func (k Keeper) importContractState(ctx sdk.Context, contractAddress sdk.AccAddress, models []types.Model) error { 911 prefixStoreKey := types.GetContractStorePrefix(contractAddress) 912 prefixStore := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), prefixStoreKey) 913 for _, model := range models { 914 if model.Value == nil { 915 model.Value = []byte{} 916 } 917 if prefixStore.Has(model.Key) { 918 return sdkerrors.Wrapf(types.ErrDuplicate, "duplicate key: %x", model.Key) 919 } 920 prefixStore.Set(model.Key, model.Value) 921 } 922 return nil 923 } 924 925 func (k Keeper) GetCodeInfo(ctx sdk.Context, codeID uint64) *types.CodeInfo { 926 927 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 928 var codeInfo types.CodeInfo 929 codeInfoBz := store.Get(types.GetCodeKey(codeID)) 930 if codeInfoBz == nil { 931 return nil 932 } 933 k.cdc.GetProtocMarshal().MustUnmarshal(codeInfoBz, &codeInfo) 934 return &codeInfo 935 } 936 937 func (k Keeper) containsCodeInfo(ctx sdk.Context, codeID uint64) bool { 938 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 939 return store.Has(types.GetCodeKey(codeID)) 940 } 941 942 func (k Keeper) IterateCodeInfos(ctx sdk.Context, cb func(uint64, types.CodeInfo) bool) { 943 prefixStore := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), types.CodeKeyPrefix) 944 945 iter := prefixStore.Iterator(nil, nil) 946 defer iter.Close() 947 948 for ; iter.Valid(); iter.Next() { 949 var c types.CodeInfo 950 k.cdc.GetProtocMarshal().MustUnmarshal(iter.Value(), &c) 951 // cb returns true to stop early 952 if cb(binary.BigEndian.Uint64(iter.Key()), c) { 953 return 954 } 955 } 956 } 957 958 func (k Keeper) GetByteCode(ctx sdk.Context, codeID uint64) ([]byte, error) { 959 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 960 961 var codeInfo types.CodeInfo 962 codeInfoBz := store.Get(types.GetCodeKey(codeID)) 963 if codeInfoBz == nil { 964 return nil, nil 965 } 966 k.cdc.GetProtocMarshal().MustUnmarshal(codeInfoBz, &codeInfo) 967 return k.wasmVM.GetCode(codeInfo.CodeHash) 968 } 969 970 // PinCode pins the wasm contract in wasmvm cache 971 func (k Keeper) pinCode(ctx sdk.Context, codeID uint64) error { 972 codeInfo := k.GetCodeInfo(ctx, codeID) 973 if codeInfo == nil { 974 return sdkerrors.Wrap(types.ErrNotFound, "code info") 975 } 976 977 if err := k.wasmVM.Pin(codeInfo.CodeHash); err != nil { 978 return sdkerrors.Wrap(types.ErrPinContractFailed, err.Error()) 979 } 980 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 981 982 // store 1 byte to not run into `nil` debugging issues 983 store.Set(types.GetPinnedCodeIndexPrefix(codeID), []byte{1}) 984 985 ctx.EventManager().EmitEvent(sdk.NewEvent( 986 types.EventTypePinCode, 987 sdk.NewAttribute(types.AttributeKeyCodeID, strconv.FormatUint(codeID, 10)), 988 )) 989 return nil 990 } 991 992 // UnpinCode removes the wasm contract from wasmvm cache 993 func (k Keeper) unpinCode(ctx sdk.Context, codeID uint64) error { 994 codeInfo := k.GetCodeInfo(ctx, codeID) 995 if codeInfo == nil { 996 return sdkerrors.Wrap(types.ErrNotFound, "code info") 997 } 998 if err := k.wasmVM.Unpin(codeInfo.CodeHash); err != nil { 999 return sdkerrors.Wrap(types.ErrUnpinContractFailed, err.Error()) 1000 } 1001 1002 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 1003 1004 store.Delete(types.GetPinnedCodeIndexPrefix(codeID)) 1005 1006 ctx.EventManager().EmitEvent(sdk.NewEvent( 1007 types.EventTypeUnpinCode, 1008 sdk.NewAttribute(types.AttributeKeyCodeID, strconv.FormatUint(codeID, 10)), 1009 )) 1010 return nil 1011 } 1012 1013 // IsPinnedCode returns true when codeID is pinned in wasmvm cache 1014 func (k Keeper) IsPinnedCode(ctx sdk.Context, codeID uint64) bool { 1015 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 1016 return store.Has(types.GetPinnedCodeIndexPrefix(codeID)) 1017 } 1018 1019 // InitializePinnedCodes updates wasmvm to pin to cache all contracts marked as pinned 1020 func (k Keeper) InitializePinnedCodes(ctx sdk.Context) error { 1021 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), types.PinnedCodeIndexPrefix) 1022 1023 iter := store.Iterator(nil, nil) 1024 defer iter.Close() 1025 1026 for ; iter.Valid(); iter.Next() { 1027 codeInfo := k.GetCodeInfo(ctx, types.ParsePinnedCodeIndex(iter.Key())) 1028 if codeInfo == nil { 1029 return sdkerrors.Wrap(types.ErrNotFound, "code info") 1030 } 1031 if err := k.wasmVM.Pin(codeInfo.CodeHash); err != nil { 1032 return sdkerrors.Wrap(types.ErrPinContractFailed, err.Error()) 1033 } 1034 } 1035 return nil 1036 } 1037 1038 // setContractInfoExtension updates the extension point data that is stored with the contract info 1039 func (k Keeper) setContractInfoExtension(ctx sdk.Context, contractAddr sdk.AccAddress, ext types.ContractInfoExtension) error { 1040 info := k.GetContractInfo(ctx, contractAddr) 1041 if info == nil { 1042 return sdkerrors.Wrap(types.ErrNotFound, "contract info") 1043 } 1044 if err := info.SetExtension(ext); err != nil { 1045 return err 1046 } 1047 k.storeContractInfo(ctx, contractAddr, info) 1048 return nil 1049 } 1050 1051 // setAccessConfig updates the access config of a code id. 1052 func (k Keeper) setAccessConfig(ctx sdk.Context, codeID uint64, config types.AccessConfig) error { 1053 info := k.GetCodeInfo(ctx, codeID) 1054 if info == nil { 1055 return sdkerrors.Wrap(types.ErrNotFound, "code info") 1056 } 1057 info.InstantiateConfig = config 1058 k.storeCodeInfo(ctx, codeID, *info) 1059 return nil 1060 } 1061 1062 // handleContractResponse processes the contract response data by emitting events and sending sub-/messages. 1063 func (k *Keeper) handleContractResponse( 1064 ctx sdk.Context, 1065 contractAddr sdk.AccAddress, 1066 ibcPort string, 1067 msgs []wasmvmtypes.SubMsg, 1068 attrs []wasmvmtypes.EventAttribute, 1069 data []byte, 1070 evts wasmvmtypes.Events, 1071 ) ([]byte, error) { 1072 attributeGasCost := k.gasRegister.EventCosts(attrs, evts) 1073 ctx.GasMeter().ConsumeGas(attributeGasCost, "Custom contract event attributes") 1074 // emit all events from this contract itself 1075 if len(attrs) != 0 { 1076 wasmEvents, err := newWasmModuleEvent(attrs, contractAddr) 1077 if err != nil { 1078 return nil, err 1079 } 1080 ctx.EventManager().EmitEvents(wasmEvents) 1081 } 1082 if len(evts) > 0 { 1083 customEvents, err := newCustomEvents(evts, contractAddr) 1084 if err != nil { 1085 return nil, err 1086 } 1087 ctx.EventManager().EmitEvents(customEvents) 1088 } 1089 return k.wasmVMResponseHandler.Handle(ctx, contractAddr, ibcPort, msgs, data) 1090 } 1091 1092 func (k Keeper) runtimeGasForContract(ctx sdk.Context) uint64 { 1093 meter := ctx.GasMeter() 1094 if meter.IsOutOfGas() { 1095 return 0 1096 } 1097 if meter.Limit() == 0 { // infinite gas meter with limit=0 and not out of gas 1098 return math.MaxUint64 1099 } 1100 return k.gasRegister.ToWasmVMGas(meter.Limit() - meter.GasConsumedToLimit()) 1101 } 1102 1103 func (k Keeper) consumeRuntimeGas(ctx sdk.Context, gas uint64) { 1104 consumed := k.gasRegister.FromWasmVMGas(gas) 1105 ctx.GasMeter().ConsumeGas(consumed, "wasm contract") 1106 // throw OutOfGas error if we ran out (got exactly to zero due to better limit enforcing) 1107 if ctx.GasMeter().IsOutOfGas() { 1108 panic(sdk.ErrorOutOfGas{Descriptor: "Wasmer function execution"}) 1109 } 1110 } 1111 1112 // generates a contract address from codeID + instanceID 1113 func (k Keeper) generateContractAddress(ctx sdk.Context, codeID uint64) sdk.AccAddress { 1114 instanceID := k.autoIncrementID(ctx, types.KeyLastInstanceID) 1115 return BuildContractAddress(codeID, instanceID) 1116 } 1117 1118 // BuildContractAddress builds an sdk account address for a contract. 1119 func BuildContractAddress(codeID, instanceID uint64) sdk.AccAddress { 1120 contractID := make([]byte, 16) 1121 binary.BigEndian.PutUint64(contractID[:8], codeID) 1122 binary.BigEndian.PutUint64(contractID[8:], instanceID) 1123 return types.Module(types.ModuleName, contractID)[:types.ContractAddrLen] 1124 } 1125 1126 func (k Keeper) autoIncrementID(ctx sdk.Context, lastIDKey []byte) uint64 { 1127 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 1128 1129 bz := store.Get(lastIDKey) 1130 id := uint64(1) 1131 if bz != nil { 1132 id = binary.BigEndian.Uint64(bz) 1133 } 1134 bz = sdk.Uint64ToBigEndian(id + 1) 1135 store.Set(lastIDKey, bz) 1136 return id 1137 } 1138 1139 // PeekAutoIncrementID reads the current value without incrementing it. 1140 func (k Keeper) PeekAutoIncrementID(ctx sdk.Context, lastIDKey []byte) uint64 { 1141 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 1142 bz := store.Get(lastIDKey) 1143 id := uint64(1) 1144 if bz != nil { 1145 id = binary.BigEndian.Uint64(bz) 1146 } 1147 return id 1148 } 1149 1150 func (k Keeper) importAutoIncrementID(ctx sdk.Context, lastIDKey []byte, val uint64) error { 1151 store := k.ada.NewStore(ctx.GasMeter(), ctx.KVStore(k.storeKey), nil) 1152 1153 if store.Has(lastIDKey) { 1154 return sdkerrors.Wrapf(types.ErrDuplicate, "autoincrement id: %s", string(lastIDKey)) 1155 } 1156 bz := sdk.Uint64ToBigEndian(val) 1157 store.Set(lastIDKey, bz) 1158 return nil 1159 } 1160 1161 func (k Keeper) importContract(ctx sdk.Context, contractAddr sdk.AccAddress, c *types.ContractInfo, state []types.Model) error { 1162 if !k.containsCodeInfo(ctx, c.CodeID) { 1163 return sdkerrors.Wrapf(types.ErrNotFound, "code id: %d", c.CodeID) 1164 } 1165 if k.HasContractInfo(ctx, contractAddr) { 1166 return sdkerrors.Wrapf(types.ErrDuplicate, "contract: %s", contractAddr) 1167 } 1168 1169 historyEntry := c.ResetFromGenesis(ctx) 1170 k.appendToContractHistory(ctx, contractAddr, historyEntry) 1171 k.storeContractInfo(ctx, contractAddr, c) 1172 k.addToContractCodeSecondaryIndex(ctx, contractAddr, historyEntry) 1173 return k.importContractState(ctx, contractAddr, state) 1174 } 1175 1176 func (k Keeper) newQueryHandler(ctx sdk.Context, contractAddress sdk.AccAddress) QueryHandler { 1177 return NewQueryHandler(ctx, k.wasmVMQueryHandler, contractAddress, k.gasRegister) 1178 } 1179 1180 // MultipliedGasMeter wraps the GasMeter from context and multiplies all reads by out defined multiplier 1181 type MultipliedGasMeter struct { 1182 originalMeter sdk.GasMeter 1183 GasRegister GasRegister 1184 } 1185 1186 func NewMultipliedGasMeter(originalMeter sdk.GasMeter, gr GasRegister) MultipliedGasMeter { 1187 return MultipliedGasMeter{originalMeter: originalMeter, GasRegister: gr} 1188 } 1189 1190 var _ wasmvm.GasMeter = MultipliedGasMeter{} 1191 1192 func (m MultipliedGasMeter) GasConsumed() sdk.Gas { 1193 return m.GasRegister.ToWasmVMGas(m.originalMeter.GasConsumed()) 1194 } 1195 1196 func (k Keeper) gasMeter(ctx sdk.Context) MultipliedGasMeter { 1197 return NewMultipliedGasMeter(ctx.GasMeter(), k.gasRegister) 1198 } 1199 1200 // Logger returns a module-specific logger. 1201 func (k Keeper) Logger(ctx sdk.Context) log.Logger { 1202 return moduleLogger(ctx) 1203 } 1204 1205 func (k *Keeper) SetInnerTxKeeper(innertxKeeper innertx.InnerTxKeeper) { 1206 k.innertxKeeper = innertxKeeper 1207 } 1208 1209 func moduleLogger(ctx sdk.Context) log.Logger { 1210 return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) 1211 } 1212 1213 // Querier creates a new grpc querier instance 1214 func Querier(k *Keeper) *grpcQuerier { 1215 return NewGrpcQuerier(*k.cdc, k.storeKey, k, k.queryGasLimit) 1216 } 1217 1218 // QueryGasLimit returns the gas limit for smart queries. 1219 func (k Keeper) QueryGasLimit() sdk.Gas { 1220 return k.queryGasLimit 1221 } 1222 1223 // BankCoinTransferrer replicates the cosmos-sdk behaviour as in 1224 // https://github.com/fibonacci-chain/fbc/libs/cosmos-sdk/blob/v0.41.4/x/bank/keeper/msg_server.go#L26 1225 type BankCoinTransferrer struct { 1226 keeper types.BankKeeper 1227 } 1228 1229 func NewBankCoinTransferrer(keeper types.BankKeeper) BankCoinTransferrer { 1230 return BankCoinTransferrer{ 1231 keeper: keeper, 1232 } 1233 } 1234 1235 // TransferCoins transfers coins from source to destination account when coin send was enabled for them and the recipient 1236 // is not in the blocked address list. 1237 func (c BankCoinTransferrer) TransferCoins(parentCtx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amount sdk.Coins) error { 1238 em := sdk.NewEventManager() 1239 ctx := *parentCtx.SetEventManager(em) 1240 if err := c.keeper.IsSendEnabledCoins(ctx, amount...); err != nil { 1241 return err 1242 } 1243 if c.keeper.BlockedAddr(toAddr) { 1244 return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", toAddr.String()) 1245 } 1246 1247 sdkerr := c.keeper.SendCoins(ctx, fromAddr, toAddr, amount) 1248 if sdkerr != nil { 1249 return sdkerr 1250 } 1251 for _, e := range em.Events() { 1252 if e.Type == sdk.EventTypeMessage { // skip messages as we talk to the keeper directly 1253 continue 1254 } 1255 parentCtx.EventManager().EmitEvent(e) 1256 } 1257 return nil 1258 } 1259 1260 type msgDispatcher interface { 1261 DispatchSubmessages(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, msgs []wasmvmtypes.SubMsg) ([]byte, error) 1262 } 1263 1264 // DefaultWasmVMContractResponseHandler default implementation that first dispatches submessage then normal messages. 1265 // The Submessage execution may include an success/failure response handling by the contract that can overwrite the 1266 // original 1267 type DefaultWasmVMContractResponseHandler struct { 1268 md msgDispatcher 1269 } 1270 1271 func NewDefaultWasmVMContractResponseHandler(md msgDispatcher) *DefaultWasmVMContractResponseHandler { 1272 return &DefaultWasmVMContractResponseHandler{md: md} 1273 } 1274 1275 // Handle processes the data returned by a contract invocation. 1276 func (h DefaultWasmVMContractResponseHandler) Handle(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, messages []wasmvmtypes.SubMsg, origRspData []byte) ([]byte, error) { 1277 result := origRspData 1278 switch rsp, err := h.md.DispatchSubmessages(ctx, contractAddr, ibcPort, messages); { 1279 case err != nil: 1280 return nil, sdkerrors.Wrap(err, "submessages") 1281 case rsp != nil: 1282 result = rsp 1283 } 1284 return result, nil 1285 }