diff --git a/x/wasm/ibc_integration_test.go b/x/wasm/ibc_integration_test.go index 421bdf33a5..e3d4b5a75e 100644 --- a/x/wasm/ibc_integration_test.go +++ b/x/wasm/ibc_integration_test.go @@ -51,7 +51,7 @@ func TestOnChanOpenInitVersion(t *testing.T) { for name, spec := range specs { t.Run(name, func(t *testing.T) { myContract := &wasmtesting.MockIBCContractCallbacks{ - IBCChannelOpenFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { + IBCChannelOpenFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { return spec.contractRsp, 0, nil }, } @@ -110,7 +110,7 @@ func TestOnChanOpenTryVersion(t *testing.T) { for name, spec := range specs { t.Run(name, func(t *testing.T) { myContract := &wasmtesting.MockIBCContractCallbacks{ - IBCChannelOpenFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { + IBCChannelOpenFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { return spec.contractRsp, 0, nil }, } @@ -248,10 +248,10 @@ type captureAckTestContractEngine struct { // NewCaptureAckTestContractEngine constructor func NewCaptureAckTestContractEngine() *captureAckTestContractEngine { m := wasmtesting.NewIBCContractMockWasmEngine(&wasmtesting.MockIBCContractCallbacks{ - IBCChannelOpenFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { + IBCChannelOpenFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { return &wasmvmtypes.IBC3ChannelOpenResponse{}, 0, nil }, - IBCChannelConnectFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelConnectMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { + IBCChannelConnectFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelConnectMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { return &wasmvmtypes.IBCBasicResponse{}, 0, nil }, }) @@ -262,7 +262,7 @@ func NewCaptureAckTestContractEngine() *captureAckTestContractEngine { func (x *captureAckTestContractEngine) SubmitIBCPacket(t *testing.T, path *wasmibctesting.Path, chainA *wasmibctesting.TestChain, senderContractAddr sdk.AccAddress, packetData []byte) *[]byte { t.Helper() // prepare a bridge to send an ibc packet by an ordinary wasm execute message - x.MockWasmEngine.ExecuteFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + x.MockWasmEngine.ExecuteFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { return &wasmvmtypes.Response{ Messages: []wasmvmtypes.SubMsg{{ID: 1, ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{IBC: &wasmvmtypes.IBCMsg{SendPacket: &wasmvmtypes.SendPacketMsg{ ChannelID: path.EndpointA.ChannelID, Data: executeMsg, Timeout: wasmvmtypes.IBCTimeout{Block: &wasmvmtypes.IBCTimeoutBlock{Revision: 1, Height: 10000000}}, @@ -271,7 +271,7 @@ func (x *captureAckTestContractEngine) SubmitIBCPacket(t *testing.T, path *wasmi } // capture acknowledgement var gotAck []byte - x.MockWasmEngine.IBCPacketAckFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { + x.MockWasmEngine.IBCPacketAckFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { gotAck = msg.Acknowledgement.Data return &wasmvmtypes.IBCBasicResponse{}, 0, nil } diff --git a/x/wasm/keeper/ante.go b/x/wasm/keeper/ante.go index 1718c1ce11..dc63c660b9 100644 --- a/x/wasm/keeper/ante.go +++ b/x/wasm/keeper/ante.go @@ -24,9 +24,6 @@ func NewCountTXDecorator(storeKey storetypes.StoreKey) *CountTXDecorator { // The ante handler passes the counter value via sdk.Context upstream. See `types.TXCounter(ctx)` to read the value. // Simulations don't get a tx counter value assigned. func (a CountTXDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { - if simulate { - return next(ctx, tx, simulate) - } store := ctx.KVStore(a.storeKey) currentHeight := ctx.BlockHeight() @@ -39,7 +36,14 @@ func (a CountTXDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, txCounter = val } // else use `0` from above to start with } - // store next counter value for current height + + // If it is simulation we store zero as current index which is a sentinel value + // Otherwise we store actual tx counter + if simulate { + store.Set(types.TXCounterPrefix, encodeHeightCounter(currentHeight, 0)) + return next(ctx, tx, simulate) + } + store.Set(types.TXCounterPrefix, encodeHeightCounter(currentHeight, txCounter+1)) return next(types.WithTXCounter(ctx, txCounter), tx, simulate) diff --git a/x/wasm/keeper/ante_test.go b/x/wasm/keeper/ante_test.go index c1e59c4aa2..73933aa196 100644 --- a/x/wasm/keeper/ante_test.go +++ b/x/wasm/keeper/ante_test.go @@ -88,8 +88,8 @@ func TestCountTxDecorator(t *testing.T) { _, ok := types.TXCounter(ctx) assert.False(t, ok) require.True(t, simulate) - // and not stored - assert.False(t, ctx.MultiStore().GetKVStore(keyWasm).Has(types.TXCounterPrefix)) + // and is stored // NOTE: simulation works different form vanilla wasmd + assert.True(t, ctx.MultiStore().GetKVStore(keyWasm).Has(types.TXCounterPrefix)) return ctx, nil }, }, diff --git a/x/wasm/keeper/handler_plugin_test.go b/x/wasm/keeper/handler_plugin_test.go index 9fc666b101..98ad4b546e 100644 --- a/x/wasm/keeper/handler_plugin_test.go +++ b/x/wasm/keeper/handler_plugin_test.go @@ -387,7 +387,7 @@ func TestBurnCoinMessageHandlerIntegration(t *testing.T) { for name, spec := range specs { t.Run(name, func(t *testing.T) { ctx, _ = parentCtx.CacheContext() - k.wasmVM = &wasmtesting.MockWasmEngine{ExecuteFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + k.wasmVM = &wasmtesting.MockWasmEngine{ExecuteFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { return &wasmvmtypes.Response{ Messages: []wasmvmtypes.SubMsg{ {Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{Burn: &spec.msg}}, ReplyOn: wasmvmtypes.ReplyNever}, //nolint:gosec diff --git a/x/wasm/keeper/keeper.go b/x/wasm/keeper/keeper.go index 11474a14ba..374ac7f7f7 100644 --- a/x/wasm/keeper/keeper.go +++ b/x/wasm/keeper/keeper.go @@ -312,14 +312,14 @@ func (k Keeper) instantiate( // create prefixed data store // 0x03 | BuildContractAddressClassic (sdk.AccAddress) prefixStoreKey := types.GetContractStorePrefix(contractAddress) - vmStore := types.NewStoreAdapter(prefix.NewStore(ctx.KVStore(k.storeKey), prefixStoreKey)) + prefixStore := types.PrefixStoreInfo{PrefixKey: prefixStoreKey, Store: ctx.MultiStore().GetKVStore(k.storeKey)} // prepare querier querier := k.newQueryHandler(ctx, contractAddress) // instantiate wasm contract gas := k.runtimeGasForContract(ctx) - res, gasUsed, err := k.wasmVM.Instantiate(codeInfo.CodeHash, env, info, initMsg, vmStore, cosmwasmAPI, querier, k.gasMeter(ctx), gas, costJSONDeserialization) + res, gasUsed, err := k.wasmVM.Instantiate(ctx, codeInfo.CodeHash, env, info, initMsg, prefixStore, cosmwasmAPI, &querier, k.gasMeter(ctx), gas, costJSONDeserialization) k.consumeRuntimeGas(ctx, gasUsed) if err != nil { return nil, nil, errorsmod.Wrap(types.ErrInstantiateFailed, err.Error()) @@ -389,7 +389,7 @@ func (k Keeper) execute(ctx sdk.Context, contractAddress, caller sdk.AccAddress, // prepare querier querier := k.newQueryHandler(ctx, contractAddress) gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.Execute(codeInfo.CodeHash, env, info, msg, prefixStore, cosmwasmAPI, querier, k.gasMeter(ctx), gas, costJSONDeserialization) + res, gasUsed, execErr := k.wasmVM.Execute(ctx, codeInfo.CodeHash, env, info, msg, prefixStore, cosmwasmAPI, &querier, k.gasMeter(ctx), gas, costJSONDeserialization) k.consumeRuntimeGas(ctx, gasUsed) if execErr != nil { return nil, errorsmod.Wrap(types.ErrExecuteFailed, execErr.Error()) @@ -459,9 +459,9 @@ func (k Keeper) migrate( querier := k.newQueryHandler(ctx, contractAddress) prefixStoreKey := types.GetContractStorePrefix(contractAddress) - vmStore := types.NewStoreAdapter(prefix.NewStore(ctx.KVStore(k.storeKey), prefixStoreKey)) + prefixStore := types.PrefixStoreInfo{PrefixKey: prefixStoreKey, Store: ctx.MultiStore().GetKVStore(k.storeKey)} gas := k.runtimeGasForContract(ctx) - res, gasUsed, err := k.wasmVM.Migrate(newCodeInfo.CodeHash, env, msg, vmStore, cosmwasmAPI, &querier, k.gasMeter(ctx), gas, costJSONDeserialization) + res, gasUsed, err := k.wasmVM.Migrate(ctx, newCodeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, &querier, k.gasMeter(ctx), gas, costJSONDeserialization) k.consumeRuntimeGas(ctx, gasUsed) if err != nil { return nil, errorsmod.Wrap(types.ErrMigrationFailed, err.Error()) @@ -512,7 +512,7 @@ func (k Keeper) Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte // prepare querier querier := k.newQueryHandler(ctx, contractAddress) gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.Sudo(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, k.gasMeter(ctx), gas, costJSONDeserialization) + res, gasUsed, execErr := k.wasmVM.Sudo(ctx, codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, &querier, k.gasMeter(ctx), gas, costJSONDeserialization) k.consumeRuntimeGas(ctx, gasUsed) if execErr != nil { return nil, errorsmod.Wrap(types.ErrExecuteFailed, execErr.Error()) @@ -549,7 +549,7 @@ func (k Keeper) reply(ctx sdk.Context, contractAddress sdk.AccAddress, reply was querier := k.newQueryHandler(ctx, contractAddress) gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.Reply(codeInfo.CodeHash, env, reply, prefixStore, cosmwasmAPI, querier, k.gasMeter(ctx), gas, costJSONDeserialization) + res, gasUsed, execErr := k.wasmVM.Reply(ctx, codeInfo.CodeHash, env, reply, prefixStore, cosmwasmAPI, &querier, k.gasMeter(ctx), gas, costJSONDeserialization) k.consumeRuntimeGas(ctx, gasUsed) if execErr != nil { return nil, errorsmod.Wrap(types.ErrExecuteFailed, execErr.Error()) @@ -728,7 +728,7 @@ func (k Keeper) QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []b querier := k.newQueryHandler(ctx, contractAddr) env := types.NewEnv(ctx, contractAddr) - queryResult, gasUsed, qErr := k.wasmVM.Query(codeInfo.CodeHash, env, req, prefixStore, cosmwasmAPI, querier, k.gasMeter(ctx), k.runtimeGasForContract(ctx), costJSONDeserialization) + queryResult, gasUsed, qErr := k.wasmVM.Query(ctx, codeInfo.CodeHash, env, req, prefixStore, cosmwasmAPI, &querier, k.gasMeter(ctx), k.runtimeGasForContract(ctx), costJSONDeserialization) k.consumeRuntimeGas(ctx, gasUsed) if qErr != nil { return nil, errorsmod.Wrap(types.ErrQueryFailed, qErr.Error()) @@ -765,13 +765,12 @@ func (k Keeper) QueryRaw(ctx sdk.Context, contractAddress sdk.AccAddress, key [] return prefixStore.Get(key) } -// internal helper function -func (k Keeper) contractInstance(ctx sdk.Context, contractAddress sdk.AccAddress) (types.ContractInfo, types.CodeInfo, wasmvm.KVStore, error) { +func (k Keeper) contractInstance(ctx sdk.Context, contractAddress sdk.AccAddress) (types.ContractInfo, types.CodeInfo, types.PrefixStoreInfo, error) { store := ctx.KVStore(k.storeKey) contractBz := store.Get(types.GetContractAddressKey(contractAddress)) if contractBz == nil { - return types.ContractInfo{}, types.CodeInfo{}, nil, types.ErrNoSuchContractFn(contractAddress.String()). + return types.ContractInfo{}, types.CodeInfo{}, types.PrefixStoreInfo{}, types.ErrNoSuchContractFn(contractAddress.String()). Wrapf("address %s", contractAddress.String()) } var contractInfo types.ContractInfo @@ -779,14 +778,14 @@ func (k Keeper) contractInstance(ctx sdk.Context, contractAddress sdk.AccAddress codeInfoBz := store.Get(types.GetCodeKey(contractInfo.CodeID)) if codeInfoBz == nil { - return contractInfo, types.CodeInfo{}, nil, types.ErrNoSuchCodeFn(contractInfo.CodeID). + return contractInfo, types.CodeInfo{}, types.PrefixStoreInfo{}, types.ErrNoSuchCodeFn(contractInfo.CodeID). Wrapf("code id %d", contractInfo.CodeID) } var codeInfo types.CodeInfo k.cdc.MustUnmarshal(codeInfoBz, &codeInfo) prefixStoreKey := types.GetContractStorePrefix(contractAddress) - prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), prefixStoreKey) - return contractInfo, codeInfo, types.NewStoreAdapter(prefixStore), nil + prefixStore := types.PrefixStoreInfo{PrefixKey: prefixStoreKey, Store: ctx.MultiStore().GetKVStore(k.storeKey)} + return contractInfo, codeInfo, prefixStore, nil } func (k Keeper) GetContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo { diff --git a/x/wasm/keeper/keeper_cgo.go b/x/wasm/keeper/keeper_cgo.go index 743ac64de4..bc645b2538 100644 --- a/x/wasm/keeper/keeper_cgo.go +++ b/x/wasm/keeper/keeper_cgo.go @@ -62,11 +62,11 @@ func NewKeeper( // only set the wasmvm if no one set this in the options // NewVM does a lot, so better not to create it and silently drop it. if keeper.wasmVM == nil { - var err error - keeper.wasmVM, err = wasmvm.NewVM(filepath.Join(homeDir, "wasm"), availableCapabilities, contractMemoryLimit, wasmConfig.ContractDebugMode, wasmConfig.MemoryCacheSize) + wasmer, err := wasmvm.NewVM(filepath.Join(homeDir, "wasm"), availableCapabilities, contractMemoryLimit, wasmConfig.ContractDebugMode, wasmConfig.MemoryCacheSize) if err != nil { panic(err) } + keeper.wasmVM = types.NewTrackingWasmerEngine(wasmer, &types.NoOpContractGasProcessor{}) } for _, o := range postOpts { diff --git a/x/wasm/keeper/keeper_test.go b/x/wasm/keeper/keeper_test.go index 0963c730a2..1f71774bbc 100644 --- a/x/wasm/keeper/keeper_test.go +++ b/x/wasm/keeper/keeper_test.go @@ -713,7 +713,7 @@ func TestInstantiateWithContractDataResponse(t *testing.T) { ctx, keepers := CreateTestInput(t, false, AvailableCapabilities) wasmEngineMock := &wasmtesting.MockWasmEngine{ - InstantiateFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + InstantiateFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { return &wasmvmtypes.Response{Data: []byte("my-response-data")}, 0, nil }, AnalyzeCodeFn: wasmtesting.WithoutIBCAnalyzeFn, @@ -738,10 +738,10 @@ func TestInstantiateWithContractFactoryChildQueriesParent(t *testing.T) { keeper := keepers.WasmKeeper var instantiationCount int - callbacks := make([]func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error), 2) + callbacks := make([]func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error), 2) wasmEngineMock := &wasmtesting.MockWasmEngine{ // dispatch instantiation calls to callbacks - InstantiateFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + InstantiateFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { require.Greater(t, len(callbacks), instantiationCount, "unexpected call to instantiation") do := callbacks[instantiationCount] instantiationCount++ @@ -761,7 +761,7 @@ func TestInstantiateWithContractFactoryChildQueriesParent(t *testing.T) { example := StoreRandomContract(t, ctx, keepers, wasmEngineMock) // factory contract - callbacks[0] = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + callbacks[0] = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { t.Log("called factory") return &wasmvmtypes.Response{Data: []byte("parent"), Messages: []wasmvmtypes.SubMsg{ { @@ -778,9 +778,22 @@ func TestInstantiateWithContractFactoryChildQueriesParent(t *testing.T) { // child contract var capturedSenderAddr string var capturedCodeInfo []byte - callbacks[1] = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + callbacks[1] = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { t.Log("called child") capturedSenderAddr = info.Sender + + // Initialize gas tracking for contract + initialGasMeter := types.NewContractGasMeter(30000000, func(_ uint64, info types.GasConsumptionInfo) types.GasConsumptionInfo { + return types.GasConsumptionInfo{ + SDKGas: info.SDKGas * 2, + } + }, capturedSenderAddr, types.ContractOperationQuery) + + q := querier.(*QueryHandler) // get query handler context to start gas tracking otherwise an error will happen + e := types.InitializeGasTracking(&q.Ctx, &initialGasMeter) + + require.NoError(t, e, "could not start contract gas tracking") + var err error capturedCodeInfo, err = querier.Query(wasmvmtypes.QueryRequest{ Wasm: &wasmvmtypes.WasmQuery{ @@ -1468,7 +1481,7 @@ func TestIterateContractsByCode(t *testing.T) { func TestIterateContractsByCodeWithMigration(t *testing.T) { // mock migration so that it does not fail when migrate example1 to example2.codeID - mockWasmVM := wasmtesting.MockWasmEngine{MigrateFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + mockWasmVM := wasmtesting.MockWasmEngine{MigrateFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { return &wasmvmtypes.Response{}, 1, nil }} wasmtesting.MakeInstantiable(&mockWasmVM) @@ -1838,7 +1851,7 @@ func TestPinnedContractLoops(t *testing.T) { require.NoError(t, k.pinCode(ctx, example.CodeID)) var loops int anyMsg := []byte(`{}`) - mock.ExecuteFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + mock.ExecuteFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { loops++ return &wasmvmtypes.Response{ Messages: []wasmvmtypes.SubMsg{ @@ -1953,21 +1966,31 @@ func TestReply(t *testing.T) { wasmtesting.MakeInstantiable(&mock) example := SeedNewContractInstance(t, ctx, keepers, &mock) + initialGasMeter := types.NewContractGasMeter(30000000, func(_ uint64, info types.GasConsumptionInfo) types.GasConsumptionInfo { + return types.GasConsumptionInfo{ + SDKGas: info.SDKGas * 2, + } + }, example.Contract.String(), types.ContractOperationQuery) + + // { Initialization + err := types.InitializeGasTracking(&ctx, &initialGasMeter) + require.NoError(t, err, "could not start contract gas tracking") + specs := map[string]struct { - replyFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) + replyFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) expData []byte expErr bool expEvt sdk.Events }{ "all good": { - replyFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + replyFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { return &wasmvmtypes.Response{Data: []byte("foo")}, 1, nil }, expData: []byte("foo"), expEvt: sdk.Events{sdk.NewEvent("reply", sdk.NewAttribute("_contract_address", example.Contract.String()))}, }, "with query": { - replyFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + replyFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { bzRsp, err := querier.Query(wasmvmtypes.QueryRequest{ Bank: &wasmvmtypes.BankQuery{ Balance: &wasmvmtypes.BalanceQuery{Address: env.Contract.Address, Denom: "stake"}, @@ -1983,7 +2006,7 @@ func TestReply(t *testing.T) { expEvt: sdk.Events{sdk.NewEvent("reply", sdk.NewAttribute("_contract_address", example.Contract.String()))}, }, "with query error handled": { - replyFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + replyFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { bzRsp, err := querier.Query(wasmvmtypes.QueryRequest{}, 0) require.Error(t, err) assert.Nil(t, bzRsp) @@ -1993,7 +2016,7 @@ func TestReply(t *testing.T) { expEvt: sdk.Events{sdk.NewEvent("reply", sdk.NewAttribute("_contract_address", example.Contract.String()))}, }, "error": { - replyFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + replyFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { return nil, 1, errors.New("testing") }, expErr: true, @@ -2021,6 +2044,17 @@ func TestQueryIsolation(t *testing.T) { var mock wasmtesting.MockWasmEngine wasmtesting.MakeInstantiable(&mock) example := SeedNewContractInstance(t, ctx, keepers, &mock) + + initialGasMeter := types.NewContractGasMeter(30000000, func(_ uint64, info types.GasConsumptionInfo) types.GasConsumptionInfo { + return types.GasConsumptionInfo{ + SDKGas: info.SDKGas * 2, + } + }, example.Contract.String(), types.ContractOperationQuery) + + // { Initialization + err := types.InitializeGasTracking(&ctx, &initialGasMeter) + require.NoError(t, err, "could not start contract gas tracking") + WithQueryHandlerDecorator(func(other WasmVMQueryHandler) WasmVMQueryHandler { return WasmVMQueryHandlerFn(func(ctx sdk.Context, caller sdk.AccAddress, request wasmvmtypes.QueryRequest) ([]byte, error) { if request.Custom == nil { @@ -2033,7 +2067,7 @@ func TestQueryIsolation(t *testing.T) { }).apply(k) // when - mock.ReplyFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + mock.ReplyFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { _, err := querier.Query(wasmvmtypes.QueryRequest{ Custom: []byte(`{}`), }, 10000*types.DefaultGasMultiplier) diff --git a/x/wasm/keeper/options_test.go b/x/wasm/keeper/options_test.go index b26e1af7c2..4e989eca9b 100644 --- a/x/wasm/keeper/options_test.go +++ b/x/wasm/keeper/options_test.go @@ -4,7 +4,6 @@ import ( "reflect" "testing" - wasmvm "github.com/CosmWasm/wasmvm" "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -43,7 +42,7 @@ func TestConstructorOptions(t *testing.T) { }, "decorate wasmvm": { srcOpt: WithWasmEngineDecorator(func(old types.WasmEngine) types.WasmEngine { - require.IsType(t, &wasmvm.VM{}, old) + require.IsType(t, &types.TrackingWasmerEngine{}, old) return &wasmtesting.MockWasmEngine{} }), verify: func(t *testing.T, k Keeper) { diff --git a/x/wasm/keeper/querier_test.go b/x/wasm/keeper/querier_test.go index 18b38fa5d0..862f9c1ccd 100644 --- a/x/wasm/keeper/querier_test.go +++ b/x/wasm/keeper/querier_test.go @@ -197,7 +197,7 @@ func TestQuerySmartContractPanics(t *testing.T) { } for msg, spec := range specs { t.Run(msg, func(t *testing.T) { - keepers.WasmKeeper.wasmVM = &wasmtesting.MockWasmEngine{QueryFn: func(checksum wasmvm.Checksum, env wasmvmtypes.Env, queryMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) ([]byte, uint64, error) { + keepers.WasmKeeper.wasmVM = &wasmtesting.MockWasmEngine{QueryFn: func(checksum wasmvm.Checksum, env wasmvmtypes.Env, queryMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) ([]byte, uint64, error) { spec.doInContract() return nil, 0, nil }} diff --git a/x/wasm/keeper/query_plugin_integration_test.go b/x/wasm/keeper/query_plugin_integration_test.go index 83dea003b0..ce2534bf11 100644 --- a/x/wasm/keeper/query_plugin_integration_test.go +++ b/x/wasm/keeper/query_plugin_integration_test.go @@ -206,7 +206,7 @@ func TestReflectInvalidStargateQuery(t *testing.T) { // make a query on the chain, should not be whitelisted _, err = keeper.QuerySmart(ctx, contractAddr, protoQueryBz) require.Error(t, err) - require.Contains(t, err.Error(), "Unsupported query") + require.Contains(t, err.Error(), "unsupported request") // now, try to build a protobuf query protoRequest = wasmvmtypes.QueryRequest{ @@ -223,7 +223,7 @@ func TestReflectInvalidStargateQuery(t *testing.T) { // make a query on the chain, should be blacklisted _, err = keeper.QuerySmart(ctx, contractAddr, protoQueryBz) require.Error(t, err) - require.Contains(t, err.Error(), "Unsupported query") + require.Contains(t, err.Error(), "unsupported request") // and another one protoRequest = wasmvmtypes.QueryRequest{ @@ -240,7 +240,7 @@ func TestReflectInvalidStargateQuery(t *testing.T) { // make a query on the chain, should be blacklisted _, err = keeper.QuerySmart(ctx, contractAddr, protoQueryBz) require.Error(t, err) - require.Contains(t, err.Error(), "Unsupported query") + require.Contains(t, err.Error(), "unsupported request") } type reflectState struct { @@ -465,7 +465,7 @@ func TestQueryDenomsIntegration(t *testing.T) { gotData, gotErr := k.QuerySmart(ctx, contractAddr, []byte(spec.query)) if spec.expErr != nil { require.Error(t, gotErr) - assert.Contains(t, gotErr.Error(), fmt.Sprintf("codespace: %s, code: %d:", spec.expErr.Codespace(), spec.expErr.ABCICode())) + assert.Contains(t, gotErr.Error(), fmt.Sprintf("codespace: %s, code: %d", spec.expErr.Codespace(), spec.expErr.ABCICode())) return } require.NoError(t, gotErr) diff --git a/x/wasm/keeper/query_plugins.go b/x/wasm/keeper/query_plugins.go index b89afdc4c3..8b116048f9 100644 --- a/x/wasm/keeper/query_plugins.go +++ b/x/wasm/keeper/query_plugins.go @@ -47,18 +47,31 @@ type GRPCQueryRouter interface { var _ wasmvmtypes.Querier = QueryHandler{} -func (q QueryHandler) Query(request wasmvmtypes.QueryRequest, gasLimit uint64) ([]byte, error) { +func (q QueryHandler) Query(request wasmvmtypes.QueryRequest, gasLimit uint64) (res []byte, err error) { // set a limit for a subCtx sdkGas := q.gasRegister.FromWasmVMGas(gasLimit) + + if err := types.CreateNewSession(&q.Ctx, sdkGas); err != nil { + return nil, err + } + // discard all changes/ events in subCtx by not committing the cached context - subCtx, _ := q.Ctx.WithGasMeter(sdk.NewGasMeter(sdkGas)).CacheContext() + subCtx, _ := q.Ctx.CacheContext() //Instead, use prepare gas tracking sub ctx - // make sure we charge the higher level context even on panic defer func() { - q.Ctx.GasMeter().ConsumeGas(subCtx.GasMeter().GasConsumed(), "contract sub-query") + destroySessionErr := types.DestroySession(&q.Ctx) + if destroySessionErr != nil { + q.Ctx.Logger().Error("error while destroying a gas tracking session", "error", destroySessionErr) + } + + if err != nil { + err = fmt.Errorf("error while querying from wasm smart contract, querier error: %s, error: %s", err, destroySessionErr) + } else { + err = destroySessionErr + } }() - res, err := q.Plugins.HandleQuery(subCtx, q.Caller, request) + res, err = q.Plugins.HandleQuery(subCtx, q.Caller, request) if err == nil { // short-circuit, the rest is dealing with handling existing errors return res, nil @@ -79,6 +92,10 @@ func (q QueryHandler) GasConsumed() uint64 { return q.gasRegister.ToWasmVMGas(q.Ctx.GasMeter().GasConsumed()) } +func (q *QueryHandler) GetCtx() *sdk.Context { + return &q.Ctx +} + type CustomQuerier func(ctx sdk.Context, request json.RawMessage) ([]byte, error) type QueryPlugins struct { diff --git a/x/wasm/keeper/query_plugins_test.go b/x/wasm/keeper/query_plugins_test.go index 78666f3cd3..b7b7e8f70b 100644 --- a/x/wasm/keeper/query_plugins_test.go +++ b/x/wasm/keeper/query_plugins_test.go @@ -10,8 +10,6 @@ import ( "time" wasmvmtypes "github.com/CosmWasm/wasmvm/types" - dbm "github.com/cometbft/cometbft-db" - "github.com/cometbft/cometbft/libs/log" "github.com/cometbft/cometbft/libs/rand" tmproto "github.com/cometbft/cometbft/proto/tendermint/types" "github.com/cosmos/gogoproto/proto" @@ -25,7 +23,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/address" "github.com/cosmos/cosmos-sdk/types/query" @@ -534,6 +531,18 @@ func TestCodeInfoWasmQuerier(t *testing.T) { } func TestQueryErrors(t *testing.T) { + ctx, _ := keeper.CreateTestInput(t, false, keeper.AvailableCapabilities) + initialGasMeter := types.NewContractGasMeter(30000000, func(_ uint64, info types.GasConsumptionInfo) types.GasConsumptionInfo { + return types.GasConsumptionInfo{ + SDKGas: info.SDKGas * 2, + } + }, "foo", types.ContractOperationQuery) + + // { Initialization + err := types.InitializeGasTracking(&ctx, &initialGasMeter) + require.NoError(t, err, "could not start contract gas tracking") + var e error = nil + specs := map[string]struct { src error expErr error @@ -541,19 +550,19 @@ func TestQueryErrors(t *testing.T) { "no error": {}, "no such contract": { src: types.ErrNoSuchContractFn("contract-addr"), - expErr: wasmvmtypes.NoSuchContract{Addr: "contract-addr"}, + expErr: fmt.Errorf("error while querying from wasm smart contract, querier error: %s, error: %s", wasmvmtypes.NoSuchContract{Addr: "contract-addr"}, e), }, "no such contract - wrapped": { src: errorsmod.Wrap(types.ErrNoSuchContractFn("contract-addr"), "my additional data"), - expErr: wasmvmtypes.NoSuchContract{Addr: "contract-addr"}, + expErr: fmt.Errorf("error while querying from wasm smart contract, querier error: %s, error: %s", wasmvmtypes.NoSuchContract{Addr: "contract-addr"}, e), }, "no such code": { src: types.ErrNoSuchCodeFn(123), - expErr: wasmvmtypes.NoSuchCode{CodeID: 123}, + expErr: fmt.Errorf("error while querying from wasm smart contract, querier error: %s, error: %s", wasmvmtypes.NoSuchCode{CodeID: 123}, e), }, "no such code - wrapped": { src: errorsmod.Wrap(types.ErrNoSuchCodeFn(123), "my additional data"), - expErr: wasmvmtypes.NoSuchCode{CodeID: 123}, + expErr: fmt.Errorf("error while querying from wasm smart contract, querier error: %s, error: %s", wasmvmtypes.NoSuchCode{CodeID: 123}, e), }, } for name, spec := range specs { @@ -561,7 +570,6 @@ func TestQueryErrors(t *testing.T) { mock := keeper.WasmVMQueryHandlerFn(func(ctx sdk.Context, caller sdk.AccAddress, request wasmvmtypes.QueryRequest) ([]byte, error) { return nil, spec.src }) - ctx := sdk.Context{}.WithGasMeter(sdk.NewInfiniteGasMeter()).WithMultiStore(store.NewCommitMultiStore(dbm.NewMemDB())).WithLogger(log.TestingLogger()) q := keeper.NewQueryHandler(ctx, mock, sdk.AccAddress{}, types.NewDefaultWasmGasRegister()) _, gotErr := q.Query(wasmvmtypes.QueryRequest{}, 1) assert.Equal(t, spec.expErr, gotErr) diff --git a/x/wasm/keeper/recurse_test.go b/x/wasm/keeper/recurse_test.go index 4d10d12e64..edc5ba7092 100644 --- a/x/wasm/keeper/recurse_test.go +++ b/x/wasm/keeper/recurse_test.go @@ -261,8 +261,8 @@ func TestLimitRecursiveQueryGas(t *testing.T) { }, expectQueriesFromContract: 10, expectOutOfGas: false, - expectError: "query wasm contract failed", // Error we get from the contract instance doing the failing query, not wasmd - expectedGas: 10*(GasWork2k+GasReturnHashed) - 249, + expectError: "query wasm contract failed", // Error we get from the contract instance doing the failing query, not wasmd + expectedGas: 10*(GasWork2k+GasReturnHashed) - 249 + 901, // NOTE: investigation on this is required, higher consumption than vanila wasmd, also needs quantification instead of arbitrary }, } @@ -299,7 +299,7 @@ func TestLimitRecursiveQueryGas(t *testing.T) { require.NoError(t, err) } if types.EnableGasVerification { - assert.Equal(t, tc.expectedGas, ctx.GasMeter().GasConsumed()) + assert.Equal(t, tc.expectedGas, ctx.GasMeter().GasConsumed()) // likely because tracking ? } assert.Equal(t, tc.expectQueriesFromContract, totalWasmQueryCounter) }) diff --git a/x/wasm/keeper/relay.go b/x/wasm/keeper/relay.go index 209218b86c..9c5805046a 100644 --- a/x/wasm/keeper/relay.go +++ b/x/wasm/keeper/relay.go @@ -37,7 +37,7 @@ func (k Keeper) OnOpenChannel( querier := k.newQueryHandler(ctx, contractAddr) gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.IBCChannelOpen(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + res, gasUsed, execErr := k.wasmVM.IBCChannelOpen(ctx, codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, &querier, ctx.GasMeter(), gas, costJSONDeserialization) k.consumeRuntimeGas(ctx, gasUsed) if execErr != nil { return "", errorsmod.Wrap(types.ErrExecuteFailed, execErr.Error()) @@ -70,7 +70,7 @@ func (k Keeper) OnConnectChannel( querier := k.newQueryHandler(ctx, contractAddr) gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.IBCChannelConnect(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + res, gasUsed, execErr := k.wasmVM.IBCChannelConnect(ctx, codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, &querier, ctx.GasMeter(), gas, costJSONDeserialization) k.consumeRuntimeGas(ctx, gasUsed) if execErr != nil { return errorsmod.Wrap(types.ErrExecuteFailed, execErr.Error()) @@ -101,7 +101,7 @@ func (k Keeper) OnCloseChannel( querier := k.newQueryHandler(ctx, contractAddr) gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.IBCChannelClose(codeInfo.CodeHash, params, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + res, gasUsed, execErr := k.wasmVM.IBCChannelClose(ctx, codeInfo.CodeHash, params, msg, prefixStore, cosmwasmAPI, &querier, ctx.GasMeter(), gas, costJSONDeserialization) k.consumeRuntimeGas(ctx, gasUsed) if execErr != nil { return errorsmod.Wrap(types.ErrExecuteFailed, execErr.Error()) @@ -131,7 +131,7 @@ func (k Keeper) OnRecvPacket( querier := k.newQueryHandler(ctx, contractAddr) gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.IBCPacketReceive(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + res, gasUsed, execErr := k.wasmVM.IBCPacketReceive(ctx, codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, &querier, ctx.GasMeter(), gas, costJSONDeserialization) k.consumeRuntimeGas(ctx, gasUsed) if execErr != nil { panic(execErr) // let the contract fully abort an IBC packet receive. @@ -189,7 +189,7 @@ func (k Keeper) OnAckPacket( querier := k.newQueryHandler(ctx, contractAddr) gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.IBCPacketAck(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + res, gasUsed, execErr := k.wasmVM.IBCPacketAck(ctx, codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, &querier, ctx.GasMeter(), gas, costJSONDeserialization) k.consumeRuntimeGas(ctx, gasUsed) if execErr != nil { return errorsmod.Wrap(types.ErrExecuteFailed, execErr.Error()) @@ -216,7 +216,7 @@ func (k Keeper) OnTimeoutPacket( querier := k.newQueryHandler(ctx, contractAddr) gas := k.runtimeGasForContract(ctx) - res, gasUsed, execErr := k.wasmVM.IBCPacketTimeout(codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, querier, ctx.GasMeter(), gas, costJSONDeserialization) + res, gasUsed, execErr := k.wasmVM.IBCPacketTimeout(ctx, codeInfo.CodeHash, env, msg, prefixStore, cosmwasmAPI, &querier, ctx.GasMeter(), gas, costJSONDeserialization) k.consumeRuntimeGas(ctx, gasUsed) if execErr != nil { return errorsmod.Wrap(types.ErrExecuteFailed, execErr.Error()) diff --git a/x/wasm/keeper/relay_test.go b/x/wasm/keeper/relay_test.go index b1ee4f3438..6b4c26ed1b 100644 --- a/x/wasm/keeper/relay_test.go +++ b/x/wasm/keeper/relay_test.go @@ -57,7 +57,7 @@ func TestOnOpenChannel(t *testing.T) { t.Run(name, func(t *testing.T) { myChannel := wasmvmtypes.IBCChannel{Version: "my test channel"} myMsg := wasmvmtypes.IBCChannelOpenMsg{OpenTry: &wasmvmtypes.IBCOpenTry{Channel: myChannel, CounterpartyVersion: "foo"}} - m.IBCChannelOpenFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { + m.IBCChannelOpenFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { assert.Equal(t, myMsg, msg) return &wasmvmtypes.IBC3ChannelOpenResponse{}, spec.contractGas * types.DefaultGasMultiplier, spec.contractErr } @@ -154,7 +154,7 @@ func TestOnConnectChannel(t *testing.T) { t.Run(name, func(t *testing.T) { myChannel := wasmvmtypes.IBCChannel{Version: "my test channel"} myMsg := wasmvmtypes.IBCChannelConnectMsg{OpenConfirm: &wasmvmtypes.IBCOpenConfirm{Channel: myChannel}} - m.IBCChannelConnectFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelConnectMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { + m.IBCChannelConnectFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelConnectMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { assert.Equal(t, msg, myMsg) return spec.contractResp, myContractGas * types.DefaultGasMultiplier, spec.contractErr } @@ -266,7 +266,7 @@ func TestOnCloseChannel(t *testing.T) { t.Run(name, func(t *testing.T) { myChannel := wasmvmtypes.IBCChannel{Version: "my test channel"} myMsg := wasmvmtypes.IBCChannelCloseMsg{CloseInit: &wasmvmtypes.IBCCloseInit{Channel: myChannel}} - m.IBCChannelCloseFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelCloseMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { + m.IBCChannelCloseFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelCloseMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { assert.Equal(t, msg, myMsg) return spec.contractResp, myContractGas * types.DefaultGasMultiplier, spec.contractErr } @@ -323,7 +323,7 @@ func TestOnRecvPacket(t *testing.T) { contractResp *wasmvmtypes.IBCReceiveResult contractErr error overwriteMessenger *wasmtesting.MockMessageHandler - mockReplyFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) + mockReplyFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) expContractGas sdk.Gas expAck []byte expErr bool @@ -424,7 +424,7 @@ func TestOnRecvPacket(t *testing.T) { Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyAlways, Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{}}}}, }, }, - mockReplyFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + mockReplyFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { return &wasmvmtypes.Response{Data: []byte("myBetterAck")}, 0, nil }, expAck: []byte("myBetterAck"), @@ -439,7 +439,7 @@ func TestOnRecvPacket(t *testing.T) { t.Run(name, func(t *testing.T) { myPacket := wasmvmtypes.IBCPacket{Data: []byte("my data")} - m.IBCPacketReceiveFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { + m.IBCPacketReceiveFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { assert.Equal(t, myPacket, msg.Packet) return spec.contractResp, myContractGas * types.DefaultGasMultiplier, spec.contractErr } @@ -565,7 +565,7 @@ func TestOnAckPacket(t *testing.T) { for name, spec := range specs { t.Run(name, func(t *testing.T) { myAck := wasmvmtypes.IBCPacketAckMsg{Acknowledgement: wasmvmtypes.IBCAcknowledgement{Data: []byte("myAck")}} - m.IBCPacketAckFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { + m.IBCPacketAckFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { assert.Equal(t, myAck, msg) return spec.contractResp, myContractGas * types.DefaultGasMultiplier, spec.contractErr } @@ -685,7 +685,7 @@ func TestOnTimeoutPacket(t *testing.T) { for name, spec := range specs { t.Run(name, func(t *testing.T) { myPacket := wasmvmtypes.IBCPacket{Data: []byte("my test packet")} - m.IBCPacketTimeoutFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketTimeoutMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { + m.IBCPacketTimeoutFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketTimeoutMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { assert.Equal(t, myPacket, msg.Packet) return spec.contractResp, myContractGas * types.DefaultGasMultiplier, spec.contractErr } diff --git a/x/wasm/keeper/submsg_test.go b/x/wasm/keeper/submsg_test.go index fe301dc37b..89bd9c33a7 100644 --- a/x/wasm/keeper/submsg_test.go +++ b/x/wasm/keeper/submsg_test.go @@ -566,7 +566,7 @@ func TestInstantiateGovSubMsgAuthzPropagated(t *testing.T) { wasmtesting.MakeInstantiable(mockWasmVM) var instanceLevel int // mock wasvm to return new instantiate msgs with the response - mockWasmVM.InstantiateFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + mockWasmVM.InstantiateFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { if instanceLevel == 2 { return &wasmvmtypes.Response{}, 0, nil } @@ -653,7 +653,7 @@ func TestMigrateGovSubMsgAuthzPropagated(t *testing.T) { var instanceLevel int // mock wasvm to return new migrate msgs with the response - mockWasmVM.MigrateFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + mockWasmVM.MigrateFn = func(codeID wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { if instanceLevel == 1 { return &wasmvmtypes.Response{}, 0, nil } diff --git a/x/wasm/keeper/wasmtesting/mock_engine.go b/x/wasm/keeper/wasmtesting/mock_engine.go index b1c7b11d04..9d3a43b5bf 100644 --- a/x/wasm/keeper/wasmtesting/mock_engine.go +++ b/x/wasm/keeper/wasmtesting/mock_engine.go @@ -10,6 +10,8 @@ import ( errorsmod "cosmossdk.io/errors" "github.com/CosmWasm/wasmd/x/wasm/types" + + sdk "github.com/cosmos/cosmos-sdk/types" ) var _ types.WasmEngine = &MockWasmEngine{} @@ -20,61 +22,66 @@ type MockWasmEngine struct { StoreCodeFn func(codeID wasmvm.WasmCode) (wasmvm.Checksum, error) StoreCodeUncheckedFn func(codeID wasmvm.WasmCode) (wasmvm.Checksum, error) AnalyzeCodeFn func(codeID wasmvm.Checksum) (*wasmvmtypes.AnalysisReport, error) - InstantiateFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) - ExecuteFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) - QueryFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, queryMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) ([]byte, uint64, error) - MigrateFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) - SudoFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, sudoMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) - ReplyFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) + InstantiateFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) + ExecuteFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) + QueryFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, queryMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) ([]byte, uint64, error) + MigrateFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) + SudoFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, sudoMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) + ReplyFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) GetCodeFn func(codeID wasmvm.Checksum) (wasmvm.WasmCode, error) CleanupFn func() - IBCChannelOpenFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) - IBCChannelConnectFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelConnectMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) - IBCChannelCloseFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelCloseMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) - IBCPacketReceiveFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) - IBCPacketAckFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) - IBCPacketTimeoutFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketTimeoutMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) + IBCChannelOpenFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) + IBCChannelConnectFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelConnectMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) + IBCChannelCloseFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelCloseMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) + IBCPacketReceiveFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) + IBCPacketAckFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) + IBCPacketTimeoutFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketTimeoutMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) PinFn func(checksum wasmvm.Checksum) error UnpinFn func(checksum wasmvm.Checksum) error GetMetricsFn func() (*wasmvmtypes.Metrics, error) } -func (m *MockWasmEngine) IBCChannelOpen(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { +func (m *MockWasmEngine) SetGasRecorder(gasRecorder types.ContractGasProcessor) { + //TODO implement me + panic("implement me") +} + +func (m *MockWasmEngine) IBCChannelOpen(ctx sdk.Context, codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { if m.IBCChannelOpenFn == nil { panic("not supposed to be called!") } return m.IBCChannelOpenFn(codeID, env, msg, store, goapi, querier, gasMeter, gasLimit, deserCost) } -func (m *MockWasmEngine) IBCChannelConnect(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelConnectMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (m *MockWasmEngine) IBCChannelConnect(ctx sdk.Context, codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelConnectMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { if m.IBCChannelConnectFn == nil { panic("not supposed to be called!") } return m.IBCChannelConnectFn(codeID, env, msg, store, goapi, querier, gasMeter, gasLimit, deserCost) } -func (m *MockWasmEngine) IBCChannelClose(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelCloseMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (m *MockWasmEngine) IBCChannelClose(ctx sdk.Context, codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelCloseMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { if m.IBCChannelCloseFn == nil { panic("not supposed to be called!") } return m.IBCChannelCloseFn(codeID, env, msg, store, goapi, querier, gasMeter, gasLimit, deserCost) } -func (m *MockWasmEngine) IBCPacketReceive(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { +func (m *MockWasmEngine) IBCPacketReceive(ctx sdk.Context, codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { if m.IBCPacketReceiveFn == nil { panic("not supposed to be called!") } return m.IBCPacketReceiveFn(codeID, env, msg, store, goapi, querier, gasMeter, gasLimit, deserCost) } -func (m *MockWasmEngine) IBCPacketAck(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (m *MockWasmEngine) IBCPacketAck(ctx sdk.Context, codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { if m.IBCPacketAckFn == nil { panic("not supposed to be called!") } return m.IBCPacketAckFn(codeID, env, msg, store, goapi, querier, gasMeter, gasLimit, deserCost) } -func (m *MockWasmEngine) IBCPacketTimeout(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketTimeoutMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (m *MockWasmEngine) IBCPacketTimeout(ctx sdk.Context, codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketTimeoutMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { if m.IBCPacketTimeoutFn == nil { panic("not supposed to be called!") } @@ -107,42 +114,42 @@ func (m *MockWasmEngine) AnalyzeCode(codeID wasmvm.Checksum) (*wasmvmtypes.Analy return m.AnalyzeCodeFn(codeID) } -func (m *MockWasmEngine) Instantiate(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { +func (m *MockWasmEngine) Instantiate(ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { if m.InstantiateFn == nil { panic("not supposed to be called!") } - return m.InstantiateFn(codeID, env, info, initMsg, store, goapi, querier, gasMeter, gasLimit, deserCost) + return m.InstantiateFn(checksum, env, info, initMsg, store, goapi, querier, gasMeter, gasLimit, deserCost) } -func (m *MockWasmEngine) Execute(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { +func (m *MockWasmEngine) Execute(ctx sdk.Context, codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { if m.ExecuteFn == nil { panic("not supposed to be called!") } return m.ExecuteFn(codeID, env, info, executeMsg, store, goapi, querier, gasMeter, gasLimit, deserCost) } -func (m *MockWasmEngine) Query(codeID wasmvm.Checksum, env wasmvmtypes.Env, queryMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) ([]byte, uint64, error) { +func (m *MockWasmEngine) Query(ctx sdk.Context, codeID wasmvm.Checksum, env wasmvmtypes.Env, queryMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) ([]byte, uint64, error) { if m.QueryFn == nil { panic("not supposed to be called!") } return m.QueryFn(codeID, env, queryMsg, store, goapi, querier, gasMeter, gasLimit, deserCost) } -func (m *MockWasmEngine) Migrate(codeID wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { +func (m *MockWasmEngine) Migrate(ctx sdk.Context, codeID wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { if m.MigrateFn == nil { panic("not supposed to be called!") } return m.MigrateFn(codeID, env, migrateMsg, store, goapi, querier, gasMeter, gasLimit, deserCost) } -func (m *MockWasmEngine) Sudo(codeID wasmvm.Checksum, env wasmvmtypes.Env, sudoMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { +func (m *MockWasmEngine) Sudo(ctx sdk.Context, codeID wasmvm.Checksum, env wasmvmtypes.Env, sudoMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { if m.SudoFn == nil { panic("not supposed to be called!") } return m.SudoFn(codeID, env, sudoMsg, store, goapi, querier, gasMeter, gasLimit, deserCost) } -func (m *MockWasmEngine) Reply(codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { +func (m *MockWasmEngine) Reply(ctx sdk.Context, codeID wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { if m.ReplyFn == nil { panic("not supposed to be called!") } @@ -193,7 +200,7 @@ func SelfCallingInstMockWasmEngine(executeCalled *bool) *MockWasmEngine { anyCodeID := bytes.Repeat([]byte{0x1}, 32) return anyCodeID, nil }, - InstantiateFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + InstantiateFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { return &wasmvmtypes.Response{ Messages: []wasmvmtypes.SubMsg{ {Msg: wasmvmtypes.CosmosMsg{ @@ -203,7 +210,7 @@ func SelfCallingInstMockWasmEngine(executeCalled *bool) *MockWasmEngine { }, 1, nil }, AnalyzeCodeFn: WithoutIBCAnalyzeFn, - ExecuteFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + ExecuteFn: func(codeID wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { *executeCalled = true return &wasmvmtypes.Response{}, 1, nil }, @@ -217,9 +224,9 @@ type IBCContractCallbacks interface { codeID wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelOpenMsg, - store wasmvm.KVStore, + store types.PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -229,9 +236,9 @@ type IBCContractCallbacks interface { codeID wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelConnectMsg, - store wasmvm.KVStore, + store types.PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -241,9 +248,9 @@ type IBCContractCallbacks interface { codeID wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelCloseMsg, - store wasmvm.KVStore, + store types.PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -253,9 +260,9 @@ type IBCContractCallbacks interface { codeID wasmvm.Checksum, env wasmvmtypes.Env, packet wasmvmtypes.IBCPacketReceiveMsg, - store wasmvm.KVStore, + store types.PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -265,9 +272,9 @@ type IBCContractCallbacks interface { codeID wasmvm.Checksum, env wasmvmtypes.Env, ack wasmvmtypes.IBCPacketAckMsg, - store wasmvm.KVStore, + store types.PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -277,9 +284,9 @@ type IBCContractCallbacks interface { codeID wasmvm.Checksum, env wasmvmtypes.Env, packet wasmvmtypes.IBCPacketTimeoutMsg, - store wasmvm.KVStore, + store types.PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -292,9 +299,9 @@ type contractExecutable interface { env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, - store wasmvm.KVStore, + store types.PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -341,7 +348,7 @@ func HashOnlyStoreCodeFn(code wasmvm.WasmCode) (wasmvm.Checksum, error) { return wasmvm.CreateChecksum(code) } -func NoOpInstantiateFn(wasmvm.Checksum, wasmvmtypes.Env, wasmvmtypes.MessageInfo, []byte, wasmvm.KVStore, wasmvm.GoAPI, wasmvm.Querier, wasmvm.GasMeter, uint64, wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { +func NoOpInstantiateFn(wasmvm.Checksum, wasmvmtypes.Env, wasmvmtypes.MessageInfo, []byte, types.PrefixStoreInfo, wasmvm.GoAPI, types.QuerierWithCtx, wasmvm.GasMeter, uint64, wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { return &wasmvmtypes.Response{}, 0, nil } @@ -362,50 +369,50 @@ func WithoutIBCAnalyzeFn(wasmvm.Checksum) (*wasmvmtypes.AnalysisReport, error) { var _ IBCContractCallbacks = &MockIBCContractCallbacks{} type MockIBCContractCallbacks struct { - IBCChannelOpenFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) - IBCChannelConnectFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelConnectMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) - IBCChannelCloseFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelCloseMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) - IBCPacketReceiveFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) - IBCPacketAckFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) - IBCPacketTimeoutFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketTimeoutMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) + IBCChannelOpenFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) + IBCChannelConnectFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelConnectMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) + IBCChannelCloseFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelCloseMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) + IBCPacketReceiveFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) + IBCPacketAckFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) + IBCPacketTimeoutFn func(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketTimeoutMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) } -func (m MockIBCContractCallbacks) IBCChannelOpen(codeID wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelOpenMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { +func (m MockIBCContractCallbacks) IBCChannelOpen(codeID wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelOpenMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { if m.IBCChannelOpenFn == nil { panic("not expected to be called") } return m.IBCChannelOpenFn(codeID, env, channel, store, goapi, querier, gasMeter, gasLimit, deserCost) } -func (m MockIBCContractCallbacks) IBCChannelConnect(codeID wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelConnectMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (m MockIBCContractCallbacks) IBCChannelConnect(codeID wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelConnectMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { if m.IBCChannelConnectFn == nil { panic("not expected to be called") } return m.IBCChannelConnectFn(codeID, env, channel, store, goapi, querier, gasMeter, gasLimit, deserCost) } -func (m MockIBCContractCallbacks) IBCChannelClose(codeID wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelCloseMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (m MockIBCContractCallbacks) IBCChannelClose(codeID wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelCloseMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { if m.IBCChannelCloseFn == nil { panic("not expected to be called") } return m.IBCChannelCloseFn(codeID, env, channel, store, goapi, querier, gasMeter, gasLimit, deserCost) } -func (m MockIBCContractCallbacks) IBCPacketReceive(codeID wasmvm.Checksum, env wasmvmtypes.Env, packet wasmvmtypes.IBCPacketReceiveMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { +func (m MockIBCContractCallbacks) IBCPacketReceive(codeID wasmvm.Checksum, env wasmvmtypes.Env, packet wasmvmtypes.IBCPacketReceiveMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { if m.IBCPacketReceiveFn == nil { panic("not expected to be called") } return m.IBCPacketReceiveFn(codeID, env, packet, store, goapi, querier, gasMeter, gasLimit, deserCost) } -func (m MockIBCContractCallbacks) IBCPacketAck(codeID wasmvm.Checksum, env wasmvmtypes.Env, ack wasmvmtypes.IBCPacketAckMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (m MockIBCContractCallbacks) IBCPacketAck(codeID wasmvm.Checksum, env wasmvmtypes.Env, ack wasmvmtypes.IBCPacketAckMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { if m.IBCPacketAckFn == nil { panic("not expected to be called") } return m.IBCPacketAckFn(codeID, env, ack, store, goapi, querier, gasMeter, gasLimit, deserCost) } -func (m MockIBCContractCallbacks) IBCPacketTimeout(codeID wasmvm.Checksum, env wasmvmtypes.Env, packet wasmvmtypes.IBCPacketTimeoutMsg, store wasmvm.KVStore, goapi wasmvm.GoAPI, querier wasmvm.Querier, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (m MockIBCContractCallbacks) IBCPacketTimeout(codeID wasmvm.Checksum, env wasmvmtypes.Env, packet wasmvmtypes.IBCPacketTimeoutMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { if m.IBCPacketTimeoutFn == nil { panic("not expected to be called") } diff --git a/x/wasm/relay_pingpong_test.go b/x/wasm/relay_pingpong_test.go index 40af77b551..02d84fe877 100644 --- a/x/wasm/relay_pingpong_test.go +++ b/x/wasm/relay_pingpong_test.go @@ -7,6 +7,9 @@ import ( wasmvm "github.com/CosmWasm/wasmvm" wasmvmtypes "github.com/CosmWasm/wasmvm/types" + "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" @@ -14,12 +17,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - sdk "github.com/cosmos/cosmos-sdk/types" - app2 "github.com/CosmWasm/wasmd/app" wasmibctesting "github.com/CosmWasm/wasmd/x/wasm/ibctesting" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" "github.com/CosmWasm/wasmd/x/wasm/keeper/wasmtesting" + "github.com/CosmWasm/wasmd/x/wasm/types" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" ) @@ -137,7 +139,7 @@ type player struct { // Execute starts the ping pong game // Contracts finds all connected channels and broadcasts a ping message -func (p *player) Execute(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ wasmvmtypes.MessageInfo, executeMsg []byte, store wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { +func (p *player) Execute(code wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { p.execCalls++ // start game var start startGame @@ -145,14 +147,16 @@ func (p *player) Execute(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ wasmvmtypes.Mes return nil, 0, err } + prefixStore := prefix.NewStore(store.Store, store.PrefixKey) + if start.MaxValue != 0 { - store.Set(maxValueKey, sdk.Uint64ToBigEndian(start.MaxValue)) + prefixStore.Set(maxValueKey, sdk.Uint64ToBigEndian(start.MaxValue)) } service := NewHit(p.actor, start.Value) p.t.Logf("[%s] starting game with: %d: %v\n", p.actor, start.Value, service) - p.incrementCounter(sentBallsCountKey, store) - store.Set(lastBallSentKey, sdk.Uint64ToBigEndian(start.Value)) + p.incrementCounter(sentBallsCountKey, prefixStore) + prefixStore.Set(lastBallSentKey, sdk.Uint64ToBigEndian(start.Value)) return &wasmvmtypes.Response{ Messages: []wasmvmtypes.SubMsg{ { @@ -175,7 +179,7 @@ func (p *player) Execute(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ wasmvmtypes.Mes } // OnIBCChannelOpen ensures to accept only configured version -func (p player) IBCChannelOpen(_ wasmvm.Checksum, _ wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { +func (p player) IBCChannelOpen(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { if msg.GetChannel().Version != p.actor { return &wasmvmtypes.IBC3ChannelOpenResponse{}, 0, nil } @@ -183,7 +187,7 @@ func (p player) IBCChannelOpen(_ wasmvm.Checksum, _ wasmvmtypes.Env, msg wasmvmt } // OnIBCChannelConnect persists connection endpoints -func (p player) IBCChannelConnect(_ wasmvm.Checksum, _ wasmvmtypes.Env, msg wasmvmtypes.IBCChannelConnectMsg, store wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (p player) IBCChannelConnect(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelConnectMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { p.storeEndpoint(store, msg.GetChannel()) return &wasmvmtypes.IBCBasicResponse{}, 0, nil } @@ -199,18 +203,18 @@ var ( // store keys maxValueKey = []byte("max-value") ) -func (p player) storeEndpoint(store wasmvm.KVStore, channel wasmvmtypes.IBCChannel) { +func (p player) storeEndpoint(store types.PrefixStoreInfo, channel wasmvmtypes.IBCChannel) { var counterparties []connectedChannelsModel - if b := store.Get(ibcEndpointsKey); b != nil { + if b := store.Store.Get(ibcEndpointsKey); b != nil { require.NoError(p.t, json.Unmarshal(b, &counterparties)) } counterparties = append(counterparties, connectedChannelsModel{Our: channel.Endpoint, Their: channel.CounterpartyEndpoint}) bz, err := json.Marshal(&counterparties) require.NoError(p.t, err) - store.Set(ibcEndpointsKey, bz) + store.Store.Set(ibcEndpointsKey, bz) } -func (p player) IBCChannelClose(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ wasmvmtypes.IBCChannelCloseMsg, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (p player) IBCChannelClose(codeID wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelCloseMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { panic("implement me") } @@ -223,7 +227,7 @@ var ( // store keys ) // IBCPacketReceive receives the hit and serves a response hit via `wasmvmtypes.IBCPacket` -func (p player) IBCPacketReceive(_ wasmvm.Checksum, _ wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, store wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { +func (p player) IBCPacketReceive(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { // parse received data and store packet := msg.Packet var receivedBall hit @@ -235,19 +239,22 @@ func (p player) IBCPacketReceive(_ wasmvm.Checksum, _ wasmvmtypes.Env, msg wasmv // no hit msg, we stop the game }, 0, nil } - p.incrementCounter(receivedBallsCountKey, store) + + prefixStore := prefix.NewStore(store.Store, store.PrefixKey) + + p.incrementCounter(receivedBallsCountKey, prefixStore) otherCount := receivedBall[counterParty(p.actor)] - store.Set(lastBallReceivedKey, sdk.Uint64ToBigEndian(otherCount)) + prefixStore.Set(lastBallReceivedKey, sdk.Uint64ToBigEndian(otherCount)) - if maxVal := store.Get(maxValueKey); maxVal != nil && otherCount > sdk.BigEndianToUint64(maxVal) { + if maxVal := prefixStore.Get(maxValueKey); maxVal != nil && otherCount > sdk.BigEndianToUint64(maxVal) { errMsg := fmt.Sprintf("max value exceeded: %d got %d", sdk.BigEndianToUint64(maxVal), otherCount) return &wasmvmtypes.IBCReceiveResult{Ok: &wasmvmtypes.IBCReceiveResponse{ Acknowledgement: receivedBall.BuildError(errMsg).GetBytes(), }}, 0, nil } - nextValue := p.incrementCounter(lastBallSentKey, store) + nextValue := p.incrementCounter(lastBallSentKey, prefixStore) newHit := NewHit(p.actor, nextValue) respHit := &wasmvmtypes.IBCMsg{SendPacket: &wasmvmtypes.SendPacketMsg{ ChannelID: packet.Src.ChannelID, @@ -257,7 +264,7 @@ func (p player) IBCPacketReceive(_ wasmvm.Checksum, _ wasmvmtypes.Env, msg wasmv Height: doNotTimeout.RevisionHeight, }}, }} - p.incrementCounter(sentBallsCountKey, store) + p.incrementCounter(sentBallsCountKey, prefixStore) p.t.Logf("[%s] received %d, returning %d: %v\n", p.actor, otherCount, nextValue, newHit) return &wasmvmtypes.IBCReceiveResult{ @@ -272,13 +279,15 @@ func (p player) IBCPacketReceive(_ wasmvm.Checksum, _ wasmvmtypes.Env, msg wasmv } // OnIBCPacketAcknowledgement handles the packet acknowledgment frame. Stops the game on an any error -func (p player) IBCPacketAck(_ wasmvm.Checksum, _ wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, store wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (p player) IBCPacketAck(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { // parse received data and store var sentBall hit if err := json.Unmarshal(msg.OriginalPacket.Data, &sentBall); err != nil { return nil, 0, err } + prefixStore := prefix.NewStore(store.Store, store.PrefixKey) + var ack hitAcknowledgement if err := json.Unmarshal(msg.Acknowledgement.Data, &ack); err != nil { return nil, 0, err @@ -290,15 +299,15 @@ func (p player) IBCPacketAck(_ wasmvm.Checksum, _ wasmvmtypes.Env, msg wasmvmtyp p.t.Logf("[%s] received app layer error: %s\n", p.actor, ack.Error) } - p.incrementCounter(confirmedBallsCountKey, store) + p.incrementCounter(confirmedBallsCountKey, prefixStore) return &wasmvmtypes.IBCBasicResponse{}, 0, nil } -func (p player) IBCPacketTimeout(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ wasmvmtypes.IBCPacketTimeoutMsg, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (p player) IBCPacketTimeout(codeID wasmvm.Checksum, env wasmvmtypes.Env, packet wasmvmtypes.IBCPacketTimeoutMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { panic("implement me") } -func (p player) incrementCounter(key []byte, store wasmvm.KVStore) uint64 { +func (p player) incrementCounter(key []byte, store store.KVStore) uint64 { var count uint64 bz := store.Get(key) if bz != nil { diff --git a/x/wasm/relay_test.go b/x/wasm/relay_test.go index 91a6a37d15..063fa17854 100644 --- a/x/wasm/relay_test.go +++ b/x/wasm/relay_test.go @@ -554,7 +554,7 @@ type captureCloseContract struct { closeCalled bool } -func (c *captureCloseContract) IBCChannelClose(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ wasmvmtypes.IBCChannelCloseMsg, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (c *captureCloseContract) IBCChannelClose(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelCloseMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { c.closeCalled = true return &wasmvmtypes.IBCBasicResponse{}, 1, nil } @@ -567,7 +567,7 @@ type sendViaIBCTransferContract struct { t *testing.T } -func (s *sendViaIBCTransferContract) Execute(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ wasmvmtypes.MessageInfo, executeMsg []byte, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { +func (s *sendViaIBCTransferContract) Execute(code wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { var in startTransfer if err := json.Unmarshal(executeMsg, &in); err != nil { return nil, 0, err @@ -597,7 +597,7 @@ type sendEmulatedIBCTransferContract struct { contractAddr string } -func (s *sendEmulatedIBCTransferContract) Execute(_ wasmvm.Checksum, _ wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { +func (s *sendEmulatedIBCTransferContract) Execute(code wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { var in startTransfer if err := json.Unmarshal(executeMsg, &in); err != nil { return nil, 0, err @@ -622,7 +622,7 @@ func (s *sendEmulatedIBCTransferContract) Execute(_ wasmvm.Checksum, _ wasmvmtyp return &wasmvmtypes.Response{Messages: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyNever, Msg: wasmvmtypes.CosmosMsg{IBC: ibcMsg}}}}, 0, nil } -func (s *sendEmulatedIBCTransferContract) IBCPacketTimeout(_ wasmvm.Checksum, _ wasmvmtypes.Env, msg wasmvmtypes.IBCPacketTimeoutMsg, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (c *sendEmulatedIBCTransferContract) IBCPacketTimeout(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketTimeoutMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { packet := msg.Packet var data ibctransfertypes.FungibleTokenPacketData @@ -650,11 +650,11 @@ type closeChannelContract struct { contractStub } -func (c *closeChannelContract) IBCChannelClose(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ wasmvmtypes.IBCChannelCloseMsg, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (c *closeChannelContract) IBCChannelClose(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelCloseMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { return &wasmvmtypes.IBCBasicResponse{}, 1, nil } -func (c *closeChannelContract) Execute(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ wasmvmtypes.MessageInfo, executeMsg []byte, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { +func (s *closeChannelContract) Execute(code wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { var in closeIBCChannel if err := json.Unmarshal(executeMsg, &in); err != nil { return nil, 0, err @@ -706,7 +706,7 @@ type ackReceiverContract struct { chain *wasmibctesting.TestChain } -func (c *ackReceiverContract) IBCPacketReceive(_ wasmvm.Checksum, _ wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { +func (c *ackReceiverContract) IBCPacketReceive(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { packet := msg.Packet var src ibctransfertypes.FungibleTokenPacketData @@ -728,7 +728,7 @@ func (c *ackReceiverContract) IBCPacketReceive(_ wasmvm.Checksum, _ wasmvmtypes. return &wasmvmtypes.IBCReceiveResult{Ok: &wasmvmtypes.IBCReceiveResponse{Acknowledgement: ack, Attributes: log}}, 0, nil } -func (c *ackReceiverContract) IBCPacketAck(_ wasmvm.Checksum, _ wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (c *ackReceiverContract) IBCPacketAck(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { var data ibctransfertypes.FungibleTokenPacketData if err := ibctransfertypes.ModuleCdc.UnmarshalJSON(msg.OriginalPacket.Data, &data); err != nil { return nil, 0, err @@ -757,7 +757,7 @@ type nackReceiverContract struct { t *testing.T } -func (c *nackReceiverContract) IBCPacketReceive(_ wasmvm.Checksum, _ wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { +func (c *nackReceiverContract) IBCPacketReceive(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { packet := msg.Packet var src ibctransfertypes.FungibleTokenPacketData @@ -774,7 +774,7 @@ type errorReceiverContract struct { t *testing.T } -func (c *errorReceiverContract) IBCPacketReceive(_ wasmvm.Checksum, _ wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { +func (c *errorReceiverContract) IBCPacketReceive(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { packet := msg.Packet var src ibctransfertypes.FungibleTokenPacketData @@ -788,27 +788,27 @@ func (c *errorReceiverContract) IBCPacketReceive(_ wasmvm.Checksum, _ wasmvmtype // simple helper struct that implements connection setup methods. type contractStub struct{} -func (s *contractStub) IBCChannelOpen(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ wasmvmtypes.IBCChannelOpenMsg, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { +func (s *contractStub) IBCChannelOpen(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelOpenMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { return &wasmvmtypes.IBC3ChannelOpenResponse{}, 0, nil } -func (s *contractStub) IBCChannelConnect(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ wasmvmtypes.IBCChannelConnectMsg, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (s *contractStub) IBCChannelConnect(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelConnectMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { return &wasmvmtypes.IBCBasicResponse{}, 0, nil } -func (s *contractStub) IBCChannelClose(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ wasmvmtypes.IBCChannelCloseMsg, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (s *contractStub) IBCChannelClose(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCChannelCloseMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { panic("implement me") } -func (s *contractStub) IBCPacketReceive(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ wasmvmtypes.IBCPacketReceiveMsg, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { +func (s *contractStub) IBCPacketReceive(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketReceiveMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { panic("implement me") } -func (s *contractStub) IBCPacketAck(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ wasmvmtypes.IBCPacketAckMsg, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (s *contractStub) IBCPacketAck(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketAckMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { return &wasmvmtypes.IBCBasicResponse{}, 0, nil } -func (s *contractStub) IBCPacketTimeout(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ wasmvmtypes.IBCPacketTimeoutMsg, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { +func (s *contractStub) IBCPacketTimeout(codeID wasmvm.Checksum, env wasmvmtypes.Env, msg wasmvmtypes.IBCPacketTimeoutMsg, store types.PrefixStoreInfo, goapi wasmvm.GoAPI, querier types.QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { panic("implement me") } diff --git a/x/wasm/types/contract_gas_meter.go b/x/wasm/types/contract_gas_meter.go new file mode 100644 index 0000000000..b4bd6e4f03 --- /dev/null +++ b/x/wasm/types/contract_gas_meter.go @@ -0,0 +1,91 @@ +package types + +import ( + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var _ sdk.GasMeter = &ContractSDKGasMeter{} + +type ContractSDKGasMeter struct { + actualGasConsumed sdk.Gas + originalGas sdk.Gas + underlyingGasMeter sdk.GasMeter + contractAddress string + contractOperation uint64 + contractGasCalculationFunc func(operationId uint64, info GasConsumptionInfo) GasConsumptionInfo +} + +func NewContractGasMeter(gasLimit uint64, gasCalculationFunc func(uint64, GasConsumptionInfo) GasConsumptionInfo, contractAddress string, contractOperation uint64) ContractSDKGasMeter { + return ContractSDKGasMeter{ + actualGasConsumed: 0, + originalGas: 0, + contractGasCalculationFunc: gasCalculationFunc, + underlyingGasMeter: sdk.NewGasMeter(gasLimit), + contractAddress: contractAddress, + contractOperation: contractOperation, + } +} + +func (c *ContractSDKGasMeter) GetContractAddress() string { + return c.contractAddress +} + +func (c *ContractSDKGasMeter) GetContractOperation() uint64 { + return c.contractOperation +} + +func (c *ContractSDKGasMeter) GetOriginalGas() sdk.Gas { + return c.originalGas +} + +func (c *ContractSDKGasMeter) GetActualGas() sdk.Gas { + return c.actualGasConsumed +} + +func (c *ContractSDKGasMeter) GasConsumed() storetypes.Gas { + return c.underlyingGasMeter.GasConsumed() +} + +func (c *ContractSDKGasMeter) GasConsumedToLimit() storetypes.Gas { + return c.underlyingGasMeter.GasConsumedToLimit() +} + +func (c *ContractSDKGasMeter) Limit() storetypes.Gas { + return c.underlyingGasMeter.Limit() +} + +func (c *ContractSDKGasMeter) ConsumeGas(amount storetypes.Gas, descriptor string) { + updatedGasInfo := c.contractGasCalculationFunc(c.contractOperation, GasConsumptionInfo{SDKGas: amount}) + c.underlyingGasMeter.ConsumeGas(updatedGasInfo.SDKGas, descriptor) + c.originalGas += amount + c.actualGasConsumed += updatedGasInfo.SDKGas +} + +func (c *ContractSDKGasMeter) RefundGas(amount storetypes.Gas, descriptor string) { + updatedGasInfo := c.contractGasCalculationFunc(c.contractOperation, GasConsumptionInfo{SDKGas: amount}) + c.underlyingGasMeter.RefundGas(updatedGasInfo.SDKGas, descriptor) + c.originalGas -= amount + c.actualGasConsumed -= updatedGasInfo.SDKGas +} + +func (c *ContractSDKGasMeter) IsPastLimit() bool { + return c.underlyingGasMeter.IsPastLimit() +} + +func (c *ContractSDKGasMeter) IsOutOfGas() bool { + return c.underlyingGasMeter.IsOutOfGas() +} + +func (c *ContractSDKGasMeter) String() string { + return c.underlyingGasMeter.String() +} + +func (c *ContractSDKGasMeter) CloneWithNewLimit(gasLimit uint64, description string) *ContractSDKGasMeter { + newContractGasMeter := NewContractGasMeter(gasLimit, c.contractGasCalculationFunc, c.contractAddress, c.contractOperation) + return &newContractGasMeter +} + +func (c *ContractSDKGasMeter) GasRemaining() uint64 { + return c.underlyingGasMeter.GasRemaining() +} diff --git a/x/wasm/types/contract_gas_recorder.go b/x/wasm/types/contract_gas_recorder.go new file mode 100644 index 0000000000..cd37e123f9 --- /dev/null +++ b/x/wasm/types/contract_gas_recorder.go @@ -0,0 +1,56 @@ +package types + +import sdk "github.com/cosmos/cosmos-sdk/types" + +const ( + ContractOperationInstantiate uint64 = iota + ContractOperationExecute + ContractOperationQuery + ContractOperationMigrate + ContractOperationSudo + ContractOperationReply + ContractOperationIbcChannelOpen + ContractOperationIbcChannelConnect + ContractOperationIbcChannelClose + ContractOperationIbcPacketReceive + ContractOperationIbcPacketAck + ContractOperationIbcPacketTimeout + ContractOperationUnknown +) + +type GasConsumptionInfo struct { + VMGas uint64 + SDKGas uint64 +} + +type ContractGasRecord struct { + OperationId uint64 + ContractAddress string + OriginalGas GasConsumptionInfo +} + +type ContractGasProcessor interface { + IngestGasRecord(ctx sdk.Context, records []ContractGasRecord) error + CalculateUpdatedGas(ctx sdk.Context, record ContractGasRecord) (GasConsumptionInfo, error) + GetGasCalculationFn(ctx sdk.Context, contractAddress string) (func(operationId uint64, gasInfo GasConsumptionInfo) GasConsumptionInfo, error) +} + +// NoOpContractGasProcessor is no-op gas processor +type NoOpContractGasProcessor struct { +} + +var _ ContractGasProcessor = &NoOpContractGasProcessor{} + +func (n *NoOpContractGasProcessor) GetGasCalculationFn(ctx sdk.Context, contractAddress string) (func(operationId uint64, gasInfo GasConsumptionInfo) GasConsumptionInfo, error) { + return func(operationId uint64, gasConsumptionInfo GasConsumptionInfo) GasConsumptionInfo { + return gasConsumptionInfo + }, nil +} + +func (n *NoOpContractGasProcessor) IngestGasRecord(_ sdk.Context, _ []ContractGasRecord) error { + return nil +} + +func (n *NoOpContractGasProcessor) CalculateUpdatedGas(_ sdk.Context, record ContractGasRecord) (GasConsumptionInfo, error) { + return record.OriginalGas, nil +} diff --git a/x/wasm/types/gas_tracking.go b/x/wasm/types/gas_tracking.go new file mode 100644 index 0000000000..07c0e0827a --- /dev/null +++ b/x/wasm/types/gas_tracking.go @@ -0,0 +1,270 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const gasTrackingKey = "__gt_key__" + +type SessionRecord struct { + ActualSDKGas sdk.Gas + OriginalSDKGas sdk.Gas + ActualVMGas sdk.Gas + OriginalVMGas sdk.Gas + ContractAddress string + ContractOperation uint64 + description string +} + +type VMRecord struct { + OriginalVMGas sdk.Gas + ActualVMGas sdk.Gas +} + +type activeSession struct { + invokedGasMeter *ContractSDKGasMeter + invokerGasMeter *ContractSDKGasMeter + gasFilledIn bool + originalVMGas sdk.Gas + actualVMGas sdk.Gas +} + +type gasTracking struct { + mainGasMeter sdk.GasMeter + activeSessions []*activeSession + sessionRecords []*SessionRecord +} + +func getGasTrackingData(ctx sdk.Context) (*gasTracking, error) { + queryTracking, ok := ctx.Value(gasTrackingKey).(*gasTracking) + if queryTracking == nil || !ok { + return nil, fmt.Errorf("unable to read query tracking value") + } + + return queryTracking, nil +} + +func currentContractGasMeter(queryTracking *gasTracking) (*ContractSDKGasMeter, error) { + if len(queryTracking.activeSessions) == 0 { + return nil, fmt.Errorf("no active sessions") + } + + lastSession := queryTracking.activeSessions[len(queryTracking.activeSessions)-1] + + if lastSession.invokedGasMeter == nil { + return nil, fmt.Errorf("no contract meter in current session") + } + + return lastSession.invokedGasMeter, nil +} + +func createCompositeKey(record *SessionRecord) string { + return record.ContractAddress + "." + fmt.Sprint(record.ContractOperation) +} + +func consolidateSessions(queryTracking *gasTracking) { + sessionRecords := queryTracking.sessionRecords + + recordSet := make(map[string]*SessionRecord) + + recordKeys := make([]string, 0) + + for _, sessionRecord := range sessionRecords { + compositeKey := createCompositeKey(sessionRecord) + existingRecord, ok := recordSet[compositeKey] + if !ok { + recordSet[compositeKey] = sessionRecord + recordKeys = append(recordKeys, compositeKey) + } else { + recordSet[compositeKey] = &SessionRecord{ + ActualSDKGas: existingRecord.ActualSDKGas + sessionRecord.ActualSDKGas, + OriginalSDKGas: existingRecord.OriginalSDKGas + sessionRecord.OriginalSDKGas, + ActualVMGas: existingRecord.ActualVMGas + sessionRecord.ActualVMGas, + OriginalVMGas: existingRecord.OriginalVMGas + sessionRecord.OriginalVMGas, + ContractAddress: sessionRecord.ContractAddress, + ContractOperation: sessionRecord.ContractOperation, + } + } + } + + queryTracking.sessionRecords = make([]*SessionRecord, len(recordKeys)) + + for i, recordKey := range recordKeys { + queryTracking.sessionRecords[i] = recordSet[recordKey] + } +} + +func doDestroyCurrentSession(ctx *sdk.Context, queryTracking *gasTracking) error { + currentSession := queryTracking.activeSessions[len(queryTracking.activeSessions)-1] + + if currentSession.invokedGasMeter != nil { + if !currentSession.gasFilledIn { + return fmt.Errorf("vm gas is not recorded in query tracking") + } + + queryTracking.mainGasMeter.ConsumeGas(currentSession.invokedGasMeter.GasConsumed(), "contract sub-query") + + queryTracking.sessionRecords = append(queryTracking.sessionRecords, &SessionRecord{ + ActualSDKGas: currentSession.invokedGasMeter.GetActualGas(), + OriginalSDKGas: currentSession.invokedGasMeter.GetOriginalGas(), + ContractAddress: currentSession.invokedGasMeter.GetContractAddress(), + ContractOperation: currentSession.invokedGasMeter.GetContractOperation(), + OriginalVMGas: currentSession.originalVMGas, + ActualVMGas: currentSession.actualVMGas, + description: "invoked", + }) + } + + if currentSession.invokerGasMeter != nil { + queryTracking.mainGasMeter.ConsumeGas(currentSession.invokerGasMeter.GasConsumed(), "query sdk gas consumption") + + queryTracking.sessionRecords = append(queryTracking.sessionRecords, &SessionRecord{ + ActualSDKGas: currentSession.invokerGasMeter.GetActualGas(), + OriginalSDKGas: currentSession.invokerGasMeter.GetOriginalGas(), + ContractAddress: currentSession.invokerGasMeter.GetContractAddress(), + ContractOperation: currentSession.invokerGasMeter.GetContractOperation(), + description: "invoker", + }) + } + + queryTracking.activeSessions = queryTracking.activeSessions[:len(queryTracking.activeSessions)-1] + + // Revert to previous gas invokedGasMeter + if len(queryTracking.activeSessions) != 0 { + *ctx = ctx.WithGasMeter(queryTracking.activeSessions[len(queryTracking.activeSessions)-1].invokedGasMeter) + } else { + *ctx = ctx.WithGasMeter(queryTracking.mainGasMeter) + } + + return nil +} + +func IsGasTrackingInitialized(ctx sdk.Context) bool { + _, err := getGasTrackingData(ctx) + return err == nil +} + +func InitializeGasTracking(ctx *sdk.Context, initialContractGasMeter *ContractSDKGasMeter) error { + data := ctx.Value(gasTrackingKey) + if data != nil { + return fmt.Errorf("query gas tracking is already initialized") + } + + queryTracking := gasTracking{ + mainGasMeter: ctx.GasMeter(), + activeSessions: []*activeSession{ + { + invokedGasMeter: initialContractGasMeter, + }, + }, + sessionRecords: nil, + } + + *ctx = ctx.WithValue(gasTrackingKey, &queryTracking) + *ctx = ctx.WithGasMeter(initialContractGasMeter) + return nil +} + +func TerminateGasTracking(ctx *sdk.Context) ([]*SessionRecord, error) { + queryTracking, err := getGasTrackingData(*ctx) + if err != nil { + return nil, err + } + + if len(queryTracking.activeSessions) != 1 { + if len(queryTracking.activeSessions) == 0 { + return nil, fmt.Errorf("internal error: the initial contract gas invokedGasMeter not found") + } else { + return nil, fmt.Errorf("internal error: multiple active gas trackers in session") + } + } + + if err := doDestroyCurrentSession(ctx, queryTracking); err != nil { + return nil, err + } + + consolidateSessions(queryTracking) + + *ctx = ctx.WithValue(gasTrackingKey, nil) + *ctx = ctx.WithGasMeter(queryTracking.mainGasMeter) + + return queryTracking.sessionRecords, nil +} + +func AddVMRecord(ctx sdk.Context, vmRecord *VMRecord) error { + queryTracking, err := getGasTrackingData(ctx) + if err != nil { + return err + } + + if len(queryTracking.activeSessions) == 0 { + return fmt.Errorf("internal error: no active sessions") + } + + lastSession := queryTracking.activeSessions[len(queryTracking.activeSessions)-1] + if lastSession.gasFilledIn { + return fmt.Errorf("gas information already present for current session") + } + + lastSession.gasFilledIn = true + lastSession.originalVMGas = vmRecord.OriginalVMGas + lastSession.actualVMGas = vmRecord.ActualVMGas + + return nil +} + +func AssociateContractMeterWithCurrentSession(ctx *sdk.Context, contractGasMeter *ContractSDKGasMeter) error { + queryTracking, err := getGasTrackingData(*ctx) + if err != nil { + return err + } + + if len(queryTracking.activeSessions) == 0 { + return fmt.Errorf("no current session found") + } + + lastSession := queryTracking.activeSessions[len(queryTracking.activeSessions)-1] + if lastSession.invokedGasMeter != nil { + return fmt.Errorf("invokedGasMeter is associated already") + } + + lastSession.invokedGasMeter = contractGasMeter + + *ctx = ctx.WithGasMeter(contractGasMeter) + return nil +} + +func CreateNewSession(ctx *sdk.Context, gasLimit uint64) error { + queryTracking, err := getGasTrackingData(*ctx) + if err != nil { + return err + } + + currentContractMeter, err := currentContractGasMeter(queryTracking) + if err != nil { + return err + } + + invokerGasMeter := currentContractMeter.CloneWithNewLimit(gasLimit, "cloned for sdk") + + queryTracking.activeSessions = append(queryTracking.activeSessions, &activeSession{ + invokerGasMeter: invokerGasMeter, + invokedGasMeter: nil, + gasFilledIn: false, + }) + + *ctx = ctx.WithGasMeter(invokerGasMeter) + + return nil +} + +func DestroySession(ctx *sdk.Context) error { + queryTracking, err := getGasTrackingData(*ctx) + if err != nil { + return err + } + + return doDestroyCurrentSession(ctx, queryTracking) +} diff --git a/x/wasm/types/gas_tracking_test.go b/x/wasm/types/gas_tracking_test.go new file mode 100644 index 0000000000..c0cc7c866b --- /dev/null +++ b/x/wasm/types/gas_tracking_test.go @@ -0,0 +1,185 @@ +package types + +import ( + "testing" + + db "github.com/cometbft/cometbft-db" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +func newContractGasMeterByRef(gasLimt uint64, gasCalculationFn func(_ uint64, info GasConsumptionInfo) GasConsumptionInfo, contractAddress string, contractOperation uint64) *ContractSDKGasMeter { + gasMeter := NewContractGasMeter(gasLimt, gasCalculationFn, contractAddress, contractOperation) + return &gasMeter +} + +func TestGasTracking(t *testing.T) { + memDB := db.NewMemDB() + cms := store.NewCommitMultiStore(memDB) + ctx := sdk.NewContext(cms, tmproto.Header{}, false, nil) + mainMeter := ctx.GasMeter() + + contracts := []string{"1contract", "2contract", "3contract"} + + initialGasMeter := NewContractGasMeter(30000000, func(_ uint64, info GasConsumptionInfo) GasConsumptionInfo { + return GasConsumptionInfo{ + SDKGas: info.SDKGas * 2, + } + }, contracts[0], ContractOperationQuery) + + // { Initialization + err := InitializeGasTracking(&ctx, &initialGasMeter) + require.NoError(t, err, "There should not be any error") + + err = InitializeGasTracking(&ctx, &initialGasMeter) + require.EqualError(t, err, "query gas tracking is already initialized", "We should not be able to initialize multiple time") + + require.Equal(t, uint64(0), mainMeter.GasConsumed(), "there should not be any consumption on main invokedGasMeter") + + // {{ Session 1 is created + err = CreateNewSession(&ctx, 1000) + require.NoError(t, err, "There should not be any error") + + require.Equal(t, uint64(0), mainMeter.GasConsumed(), "there should not be any consumption on main invokedGasMeter") + + ctx.GasMeter().ConsumeGas(400, "") + + // {{} Session 1 is destroyed + err = DestroySession(&ctx) + require.NoError(t, err, "There should not be any error") + + require.Equal(t, uint64(400*2), mainMeter.GasConsumed(), "main invokedGasMeter must have consumed 400*2 gas") + + // {{}{ Session 2 session is created + err = CreateNewSession(&ctx, 5000) + require.NoError(t, err, "There should not be an error") + + ctx.GasMeter().ConsumeGas(51, "") + + // {{}{ Session 2: Meter associated + err = AssociateContractMeterWithCurrentSession(&ctx, newContractGasMeterByRef(5000, func(_ uint64, info GasConsumptionInfo) GasConsumptionInfo { + return GasConsumptionInfo{ + SDKGas: info.SDKGas / 3, + } + }, contracts[1], ContractOperationIbcChannelConnect)) + require.NoError(t, err, "There should not be an error") + + // There should not be any error + err = AssociateContractMeterWithCurrentSession(&ctx, newContractGasMeterByRef(5000, func(_ uint64, info GasConsumptionInfo) GasConsumptionInfo { + return GasConsumptionInfo{ + SDKGas: info.SDKGas / 3, + } + }, contracts[1], ContractOperationIbcPacketAck)) + require.EqualError(t, err, "invokedGasMeter is associated already", "There should not be an error") + + ctx.GasMeter().ConsumeGas(100, "") + + // {{}{{ Session 3 created + err = CreateNewSession(&ctx, 5000) + require.NoError(t, err, "There should not be an error") + + ctx.GasMeter().ConsumeGas(33, "") + + // {{}{{ Session 3: Meter associated + err = AssociateContractMeterWithCurrentSession(&ctx, newContractGasMeterByRef(5000, func(_ uint64, info GasConsumptionInfo) GasConsumptionInfo { + return GasConsumptionInfo{ + SDKGas: info.SDKGas * 7, + } + }, contracts[2], ContractOperationIbcChannelOpen)) + require.NoError(t, err, "There should not be an error") + + ctx.GasMeter().ConsumeGas(140, "") + + ctx.GasMeter().ConsumeGas(3, "") + + // {{}{{ Session 3: Add vm record + err = AddVMRecord(ctx, &VMRecord{ + OriginalVMGas: 1, + ActualVMGas: 2, + }) + require.NoError(t, err, "We should be able to add vm record") + + err = AddVMRecord(ctx, &VMRecord{ + OriginalVMGas: 1, + ActualVMGas: 2, + }) + require.EqualError(t, err, "gas information already present for current session", "We should be able to add vm record") + + ctx.GasMeter().ConsumeGas(4, "") + + // {{}{{} Session 3: Destroyed + err = DestroySession(&ctx) + require.NoError(t, err, "There should not be any error") + + require.Equal(t, uint64(147*7)+(400*2)+uint64(33/3), mainMeter.GasConsumed(), "main invokedGasMeter must have consumed 143*7 gas") + + ctx.GasMeter().ConsumeGas(210, "") + + // {{}{{} Session 2: VM Record added + err = AddVMRecord(ctx, &VMRecord{ + OriginalVMGas: 3, + ActualVMGas: 4, + }) + require.NoError(t, err, "We should be able to add vm record") + + require.Equal(t, uint64(147*7)+(400*2)+uint64(33/3), mainMeter.GasConsumed(), "main invokedGasMeter must have consumed 143*7 gas") + + // {{}{{}} Session 2 Destroyed + err = DestroySession(&ctx) + require.NoError(t, err, "There should not be any error") + + require.Equal(t, uint64(147*7)+uint64(100/3)+uint64(210/3)+uint64(33/3)+uint64(400*2)+uint64(51*2), mainMeter.GasConsumed(), "main invokedGasMeter must have consumed 143*7 + 100/3 gas") + + // {{}{{}}{ Session 4 Created + err = CreateNewSession(&ctx, 1000) + require.NoError(t, err, "There should not be any error") + + ctx.GasMeter().ConsumeGas(400, "") + + ctx.GasMeter().ConsumeGas(100, "") + + // {{}{{}}{} Session 4 Destroyed + err = DestroySession(&ctx) + require.NoError(t, err, "There should not be any error") + + require.Equal(t, uint64(147*7)+uint64(100/3)+uint64(210/3)+uint64(33/3)+uint64(400*2)+uint64(51*2)+uint64(500*2), mainMeter.GasConsumed(), "main invokedGasMeter must have consumed 143*7 + 100/3 gas") + + // {{}{{}}{} VM Record added for initial gas invokedGasMeter + err = AddVMRecord(ctx, &VMRecord{ + OriginalVMGas: 5, + ActualVMGas: 6, + }) + require.NoError(t, err, "We should be able to add vm record") + + ctx.GasMeter().ConsumeGas(200, "") + + // {{}{{}}{}} Terminated session + sessionRecords, err := TerminateGasTracking(&ctx) + require.NoError(t, err, "We should be able to terminate") + + require.Equal(t, uint64(147*7)+uint64(100/3)+uint64(210/3)+uint64(33/3)+uint64(400*2)+uint64(51*2)+uint64(500*2)+uint64(200*2), mainMeter.GasConsumed(), "main invokedGasMeter must have consumed 143*7 + 100/3 + 850*2 gas") + + require.Equal(t, 3, len(sessionRecords), "3 gas invokedGasMeter sessions were created") + require.Equal(t, contracts[0], sessionRecords[0].ContractAddress) + require.Equal(t, uint64(1151), sessionRecords[0].OriginalSDKGas) + require.Equal(t, uint64(2302), sessionRecords[0].ActualSDKGas) + require.Equal(t, uint64(5), sessionRecords[0].OriginalVMGas) + require.Equal(t, uint64(6), sessionRecords[0].ActualVMGas) + require.Equal(t, ContractOperationQuery, sessionRecords[0].ContractOperation) + + require.Equal(t, contracts[2], sessionRecords[1].ContractAddress) + require.Equal(t, uint64(143+4), sessionRecords[1].OriginalSDKGas) + require.Equal(t, uint64(143*7+4*7), sessionRecords[1].ActualSDKGas) + require.Equal(t, uint64(1), sessionRecords[1].OriginalVMGas) + require.Equal(t, uint64(2), sessionRecords[1].ActualVMGas) + require.Equal(t, ContractOperationIbcChannelOpen, sessionRecords[1].ContractOperation) + + require.Equal(t, contracts[1], sessionRecords[2].ContractAddress) + require.Equal(t, uint64(100+210+33), sessionRecords[2].OriginalSDKGas) + require.Equal(t, uint64(33+70+11), sessionRecords[2].ActualSDKGas) + require.Equal(t, uint64(3), sessionRecords[2].OriginalVMGas) + require.Equal(t, uint64(4), sessionRecords[2].ActualVMGas) + require.Equal(t, ContractOperationIbcChannelConnect, sessionRecords[2].ContractOperation) +} diff --git a/x/wasm/types/tracking_wasmer_engine.go b/x/wasm/types/tracking_wasmer_engine.go new file mode 100644 index 0000000000..0311877925 --- /dev/null +++ b/x/wasm/types/tracking_wasmer_engine.go @@ -0,0 +1,1036 @@ +package types + +import ( + "fmt" + + wasmvm "github.com/CosmWasm/wasmvm" + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + "github.com/cosmos/cosmos-sdk/store/gaskv" + "github.com/cosmos/cosmos-sdk/store/prefix" + stypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type BareWasmVM interface { + // Create will compile the wasm code, and store the resulting pre-compile + // as well as the original code. Both can be referenced later via CodeID + // This must be done one time for given code, after which it can be + // instatitated many times, and each instance called many times. + // + // For example, the code for all ERC-20 contracts should be the same. + // This function stores the code for that contract only once, but it can + // be instantiated with custom inputs in the future. + + // Deprecated: use StoreCode instead. + Create(code wasmvm.WasmCode) (wasmvm.Checksum, error) + + // Create will compile the wasm code, and store the resulting pre-compile + // as well as the original code. Both can be referenced later via checksum + // This must be done one time for given code, after which it can be + // instatitated many times, and each instance called many times. + // It does the same as StoreCodeUnchecked plus the static checks. + StoreCode(code wasmvm.WasmCode) (wasmvm.Checksum, error) + + // Create will compile the wasm code, and store the resulting pre-compile + // as well as the original code. Both can be referenced later via checksum + // This must be done one time for given code, after which it can be + // instatitated many times, and each instance called many times. + // It does the same as StoreCode but without the static checks. + StoreCodeUnchecked(code wasmvm.WasmCode) (wasmvm.Checksum, error) + + // AnalyzeCode will statically analyze the code. + // Currently just reports if it exposes all IBC entry points. + AnalyzeCode(checksum wasmvm.Checksum) (*wasmvmtypes.AnalysisReport, error) + + // Instantiate will create a new contract based on the given codeID. + // We can set the initMsg (contract "genesis") here, and it then receives + // an account and address and can be invoked (Execute) many times. + // + // Storage should be set with a PrefixedKVStore that this code can safely access. + // + // Under the hood, we may recompile the wasm, use a cached native compile, or even use a cached instance + // for performance. + Instantiate( + checksum wasmvm.Checksum, + env wasmvmtypes.Env, + info wasmvmtypes.MessageInfo, + initMsg []byte, + store wasmvm.KVStore, + goapi wasmvm.GoAPI, + querier wasmvm.Querier, + gasMeter wasmvm.GasMeter, + gasLimit uint64, + deserCost wasmvmtypes.UFraction, + ) (*wasmvmtypes.Response, uint64, error) + + // Execute calls a given contract. Since the only difference between contracts with the same CodeID is the + // data in their local storage, and their address in the outside world, we need no ContractID here. + // (That is a detail for the external, sdk-facing, side). + // + // The caller is responsible for passing the correct `store` (which must have been initialized exactly once), + // and setting the env with relevant info on this instance (address, balance, etc) + Execute( + code wasmvm.Checksum, + env wasmvmtypes.Env, + info wasmvmtypes.MessageInfo, + executeMsg []byte, + store wasmvm.KVStore, + goapi wasmvm.GoAPI, + querier wasmvm.Querier, + gasMeter wasmvm.GasMeter, + gasLimit uint64, + deserCost wasmvmtypes.UFraction, + ) (*wasmvmtypes.Response, uint64, error) + + // Query allows a client to execute a contract-specific query. If the result is not empty, it should be + // valid json-encoded data to return to the client. + // The meaning of path and data can be determined by the code. Path is the suffix of the abci.QueryRequest.Path + Query( + code wasmvm.Checksum, + env wasmvmtypes.Env, + queryMsg []byte, + store wasmvm.KVStore, + goapi wasmvm.GoAPI, + querier wasmvm.Querier, + gasMeter wasmvm.GasMeter, + gasLimit uint64, + deserCost wasmvmtypes.UFraction, + ) ([]byte, uint64, error) + + // Migrate will migrate an existing contract to a new code binary. + // This takes storage of the data from the original contract and the CodeID of the new contract that should + // replace it. This allows it to run a migration step if needed, or return an error if unable to migrate + // the given data. + // + // MigrateMsg has some data on how to perform the migration. + Migrate( + checksum wasmvm.Checksum, + env wasmvmtypes.Env, + migrateMsg []byte, + store wasmvm.KVStore, + goapi wasmvm.GoAPI, + querier wasmvm.Querier, + gasMeter wasmvm.GasMeter, + gasLimit uint64, + deserCost wasmvmtypes.UFraction, + ) (*wasmvmtypes.Response, uint64, error) + + // Sudo runs an existing contract in read/write mode (like Execute), but is never exposed to external callers + // (either transactions or government proposals), but can only be called by other native Go modules directly. + // + // This allows a contract to expose custom "super user" functions or priviledged operations that can be + // deeply integrated with native modules. + Sudo( + checksum wasmvm.Checksum, + env wasmvmtypes.Env, + sudoMsg []byte, + store wasmvm.KVStore, + goapi wasmvm.GoAPI, + querier wasmvm.Querier, + gasMeter wasmvm.GasMeter, + gasLimit uint64, + deserCost wasmvmtypes.UFraction, + ) (*wasmvmtypes.Response, uint64, error) + + // Reply is called on the original dispatching contract after running a submessage + Reply( + checksum wasmvm.Checksum, + env wasmvmtypes.Env, + reply wasmvmtypes.Reply, + store wasmvm.KVStore, + goapi wasmvm.GoAPI, + querier wasmvm.Querier, + gasMeter wasmvm.GasMeter, + gasLimit uint64, + deserCost wasmvmtypes.UFraction, + ) (*wasmvmtypes.Response, uint64, error) + + // GetCode will load the original wasm code for the given code id. + // This will only succeed if that code id was previously returned from + // a call to Create. + // + // This can be used so that the (short) code id (hash) is stored in the iavl tree + // and the larger binary blobs (wasm and pre-compiles) are all managed by the + // rust library + GetCode(code wasmvm.Checksum) (wasmvm.WasmCode, error) + + // Cleanup should be called when no longer using this to free resources on the rust-side + Cleanup() + + // IBCChannelOpen is available on IBC-enabled contracts and is a hook to call into + // during the handshake pahse + IBCChannelOpen( + checksum wasmvm.Checksum, + env wasmvmtypes.Env, + channel wasmvmtypes.IBCChannelOpenMsg, + store wasmvm.KVStore, + goapi wasmvm.GoAPI, + querier wasmvm.Querier, + gasMeter wasmvm.GasMeter, + gasLimit uint64, + deserCost wasmvmtypes.UFraction, + ) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) + + // IBCChannelConnect is available on IBC-enabled contracts and is a hook to call into + // during the handshake pahse + IBCChannelConnect( + checksum wasmvm.Checksum, + env wasmvmtypes.Env, + channel wasmvmtypes.IBCChannelConnectMsg, + store wasmvm.KVStore, + goapi wasmvm.GoAPI, + querier wasmvm.Querier, + gasMeter wasmvm.GasMeter, + gasLimit uint64, + deserCost wasmvmtypes.UFraction, + ) (*wasmvmtypes.IBCBasicResponse, uint64, error) + + // IBCChannelClose is available on IBC-enabled contracts and is a hook to call into + // at the end of the channel lifetime + IBCChannelClose( + checksum wasmvm.Checksum, + env wasmvmtypes.Env, + channel wasmvmtypes.IBCChannelCloseMsg, + store wasmvm.KVStore, + goapi wasmvm.GoAPI, + querier wasmvm.Querier, + gasMeter wasmvm.GasMeter, + gasLimit uint64, + deserCost wasmvmtypes.UFraction, + ) (*wasmvmtypes.IBCBasicResponse, uint64, error) + + // IBCPacketReceive is available on IBC-enabled contracts and is called when an incoming + // packet is received on a channel belonging to this contract + IBCPacketReceive( + checksum wasmvm.Checksum, + env wasmvmtypes.Env, + packet wasmvmtypes.IBCPacketReceiveMsg, + store wasmvm.KVStore, + goapi wasmvm.GoAPI, + querier wasmvm.Querier, + gasMeter wasmvm.GasMeter, + gasLimit uint64, + deserCost wasmvmtypes.UFraction, + ) (*wasmvmtypes.IBCReceiveResult, uint64, error) + + // IBCPacketAck is available on IBC-enabled contracts and is called when an + // the response for an outgoing packet (previously sent by this contract) + // is received + IBCPacketAck( + checksum wasmvm.Checksum, + env wasmvmtypes.Env, + ack wasmvmtypes.IBCPacketAckMsg, + store wasmvm.KVStore, + goapi wasmvm.GoAPI, + querier wasmvm.Querier, + gasMeter wasmvm.GasMeter, + gasLimit uint64, + deserCost wasmvmtypes.UFraction, + ) (*wasmvmtypes.IBCBasicResponse, uint64, error) + + // IBCPacketTimeout is available on IBC-enabled contracts and is called when an + // outgoing packet (previously sent by this contract) will provably never be executed. + // Usually handled like ack returning an error + IBCPacketTimeout( + checksum wasmvm.Checksum, + env wasmvmtypes.Env, + packet wasmvmtypes.IBCPacketTimeoutMsg, + store wasmvm.KVStore, + goapi wasmvm.GoAPI, + querier wasmvm.Querier, + gasMeter wasmvm.GasMeter, + gasLimit uint64, + deserCost wasmvmtypes.UFraction, + ) (*wasmvmtypes.IBCBasicResponse, uint64, error) + + // Pin pins a code to an in-memory cache, such that is + // always loaded quickly when executed. + // Pin is idempotent. + Pin(checksum wasmvm.Checksum) error + + // Unpin removes the guarantee of a contract to be pinned (see Pin). + // After calling this, the code may or may not remain in memory depending on + // the implementor's choice. + // Unpin is idempotent. + Unpin(checksum wasmvm.Checksum) error + + // GetMetrics some internal metrics for monitoring purposes. + GetMetrics() (*wasmvmtypes.Metrics, error) +} + +type QueryTrackingData struct { + TrackingRecords []ContractTrackingData +} + +type ContractTrackingData struct { + ContractAddress string + Operation uint64 + GasInfo GasConsumptionInfo +} + +type TrackingVMError struct { + VmError error + GasProcessorError error +} + +func (t *TrackingVMError) Error() string { + vmErrorString := "" + gasProcessorErrorString := "" + + if t.VmError != nil { + vmErrorString = t.VmError.Error() + } + + if t.GasProcessorError != nil { + gasProcessorErrorString = t.GasProcessorError.Error() + } + + return fmt.Sprintf("error in invocation of tracking vm: WASM vm error: %s and Gas recording error: %s", + vmErrorString, gasProcessorErrorString) +} + +type TrackingWasmerEngine struct { + vm BareWasmVM + gasProcessor ContractGasProcessor +} + +// StoreCode implements WasmerEngine. +func (t *TrackingWasmerEngine) StoreCode(code wasmvm.WasmCode) (wasmvmtypes.Checksum, error) { + return t.vm.StoreCode(code) +} + +// StoreCodeUnchecked implements WasmerEngine. +func (t *TrackingWasmerEngine) StoreCodeUnchecked(code wasmvm.WasmCode) (wasmvmtypes.Checksum, error) { + return t.vm.StoreCodeUnchecked(code) +} + +func NewTrackingWasmerEngine(vm BareWasmVM, gasProcessor ContractGasProcessor) *TrackingWasmerEngine { + return &TrackingWasmerEngine{ + vm, + gasProcessor, + } +} + +func (t *TrackingWasmerEngine) SetGasRecorder(recorder ContractGasProcessor) { + t.gasProcessor = recorder +} + +func (t *TrackingWasmerEngine) getActualGas(ctx sdk.Context, operationId uint64, contractAddress string, gasInfo GasConsumptionInfo) (GasConsumptionInfo, error) { + return t.gasProcessor.CalculateUpdatedGas(ctx, ContractGasRecord{ + OperationId: operationId, + ContractAddress: contractAddress, + OriginalGas: gasInfo, + }) +} + +func (t *TrackingWasmerEngine) ingestGasRecords(ctx sdk.Context, sessionRecords []*SessionRecord) error { + gasRecords := make([]ContractGasRecord, len(sessionRecords)) + + for i, sessionRecord := range sessionRecords { + gasRecords[i] = ContractGasRecord{ + OperationId: sessionRecord.ContractOperation, + OriginalGas: GasConsumptionInfo{ + SDKGas: sessionRecord.OriginalSDKGas, + VMGas: sessionRecord.OriginalVMGas, + }, + ContractAddress: sessionRecord.ContractAddress, + } + } + + return t.gasProcessor.IngestGasRecord(ctx, gasRecords) +} + +func (t *TrackingWasmerEngine) Create(code wasmvm.WasmCode) (wasmvm.Checksum, error) { + return t.vm.Create(code) +} + +func (t *TrackingWasmerEngine) AnalyzeCode(checksum wasmvm.Checksum) (*wasmvmtypes.AnalysisReport, error) { + return t.vm.AnalyzeCode(checksum) +} + +func (t *TrackingWasmerEngine) Query(ctx sdk.Context, code wasmvm.Checksum, env wasmvmtypes.Env, queryMsg []byte, store PrefixStoreInfo, goapi wasmvm.GoAPI, querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) ([]byte, uint64, error) { + const CurrentOperation = ContractOperationQuery + var contractAddress = env.Contract.Address + + gasCalcFn, err := t.gasProcessor.GetGasCalculationFn(ctx, contractAddress) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + contractMeter := NewContractGasMeter(gasLimit, gasCalcFn, contractAddress, CurrentOperation) + + if IsGasTrackingInitialized(*querier.GetCtx()) { + err = AssociateContractMeterWithCurrentSession(querier.GetCtx(), &contractMeter) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + } else { + err = InitializeGasTracking(querier.GetCtx(), &contractMeter) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + } + + prefixStore := prefix.NewStore(gaskv.NewStore(store.Store, &contractMeter, stypes.KVGasConfig()), store.PrefixKey) + + response, vmGasUsed, err := t.vm.Query(code, env, queryMsg, NewStoreAdapter(prefixStore), goapi, querier, gasMeter, gasLimit, deserCost) + + updatedGasInfo, trackingErr := t.getActualGas(ctx, CurrentOperation, contractAddress, GasConsumptionInfo{ + SDKGas: 0, + VMGas: vmGasUsed, + }) + if trackingErr != nil { + return response, 0, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + trackingErr = AddVMRecord(*querier.GetCtx(), &VMRecord{ + OriginalVMGas: vmGasUsed, + ActualVMGas: updatedGasInfo.VMGas, + }) + + if err != nil && trackingErr == nil { + return response, updatedGasInfo.VMGas, err + } else if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } else { + return response, updatedGasInfo.VMGas, nil + } +} + +func (t *TrackingWasmerEngine) Instantiate(ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store PrefixStoreInfo, goapi wasmvm.GoAPI, querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + const CurrentOperation = ContractOperationInstantiate + var contractAddress = env.Contract.Address + + gasCalcFn, err := t.gasProcessor.GetGasCalculationFn(ctx, contractAddress) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + contractMeter := NewContractGasMeter(gasLimit, gasCalcFn, contractAddress, CurrentOperation) + + err = InitializeGasTracking(querier.GetCtx(), &contractMeter) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + prefixStore := prefix.NewStore(gaskv.NewStore(store.Store, &contractMeter, stypes.KVGasConfig()), store.PrefixKey) + + response, vmGasUsed, err := t.vm.Instantiate(checksum, env, info, initMsg, NewStoreAdapter(prefixStore), goapi, querier, gasMeter, gasLimit, deserCost) + + updatedGasInfo, trackingErr := t.getActualGas(ctx, CurrentOperation, contractAddress, GasConsumptionInfo{ + SDKGas: 0, + VMGas: vmGasUsed, + }) + if trackingErr != nil { + return response, 0, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + trackingErr = AddVMRecord(*querier.GetCtx(), &VMRecord{ + OriginalVMGas: vmGasUsed, + ActualVMGas: updatedGasInfo.VMGas, + }) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + sessionRecords, trackingErr := TerminateGasTracking(querier.GetCtx()) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + if err != nil && trackingErr == nil { + return response, updatedGasInfo.VMGas, err + } else if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + ingestingErr := t.ingestGasRecords(ctx, sessionRecords) + + if ingestingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: ingestingErr} + } else { + return response, updatedGasInfo.VMGas, nil + } +} + +func (t *TrackingWasmerEngine) Execute(ctx sdk.Context, code wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store PrefixStoreInfo, goapi wasmvm.GoAPI, querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + const CurrentOperation = ContractOperationExecute + var contractAddress = env.Contract.Address + + gasCalcFn, err := t.gasProcessor.GetGasCalculationFn(ctx, contractAddress) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + contractMeter := NewContractGasMeter(gasLimit, gasCalcFn, contractAddress, CurrentOperation) + + err = InitializeGasTracking(querier.GetCtx(), &contractMeter) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + prefixStore := prefix.NewStore(gaskv.NewStore(store.Store, &contractMeter, stypes.KVGasConfig()), store.PrefixKey) + + response, vmGasUsed, err := t.vm.Execute(code, env, info, executeMsg, NewStoreAdapter(prefixStore), goapi, querier, gasMeter, gasLimit, deserCost) + + updatedGasInfo, trackingErr := t.getActualGas(ctx, CurrentOperation, contractAddress, GasConsumptionInfo{ + SDKGas: 0, + VMGas: vmGasUsed, + }) + if trackingErr != nil { + return response, 0, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + trackingErr = AddVMRecord(*querier.GetCtx(), &VMRecord{ + OriginalVMGas: vmGasUsed, + ActualVMGas: updatedGasInfo.VMGas, + }) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + sessionRecords, trackingErr := TerminateGasTracking(querier.GetCtx()) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + if err != nil && trackingErr == nil { + return response, updatedGasInfo.VMGas, err + } else if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + ingestingErr := t.ingestGasRecords(ctx, sessionRecords) + + if ingestingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: ingestingErr} + } else { + return response, updatedGasInfo.VMGas, nil + } +} + +func (t *TrackingWasmerEngine) Migrate(ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, store PrefixStoreInfo, goapi wasmvm.GoAPI, querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + const CurrentOperation = ContractOperationMigrate + var contractAddress = env.Contract.Address + + gasCalcFn, err := t.gasProcessor.GetGasCalculationFn(ctx, contractAddress) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + contractMeter := NewContractGasMeter(gasLimit, gasCalcFn, contractAddress, CurrentOperation) + + err = InitializeGasTracking(querier.GetCtx(), &contractMeter) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + prefixStore := prefix.NewStore(gaskv.NewStore(store.Store, &contractMeter, stypes.KVGasConfig()), store.PrefixKey) + + response, vmGasUsed, err := t.vm.Migrate(checksum, env, migrateMsg, NewStoreAdapter(prefixStore), goapi, querier, gasMeter, gasLimit, deserCost) + + updatedGasInfo, trackingErr := t.getActualGas(ctx, CurrentOperation, contractAddress, GasConsumptionInfo{ + SDKGas: 0, + VMGas: vmGasUsed, + }) + if trackingErr != nil { + return response, 0, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + trackingErr = AddVMRecord(*querier.GetCtx(), &VMRecord{ + OriginalVMGas: vmGasUsed, + ActualVMGas: updatedGasInfo.VMGas, + }) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + sessionRecords, trackingErr := TerminateGasTracking(querier.GetCtx()) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + if err != nil && trackingErr == nil { + return response, updatedGasInfo.VMGas, err + } else if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + ingestingErr := t.ingestGasRecords(ctx, sessionRecords) + + if ingestingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: ingestingErr} + } else { + return response, updatedGasInfo.VMGas, nil + } +} + +func (t *TrackingWasmerEngine) Sudo(ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, sudoMsg []byte, store PrefixStoreInfo, goapi wasmvm.GoAPI, querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + const CurrentOperation = ContractOperationSudo + var contractAddress = env.Contract.Address + + gasCalcFn, err := t.gasProcessor.GetGasCalculationFn(ctx, contractAddress) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + contractMeter := NewContractGasMeter(gasLimit, gasCalcFn, contractAddress, CurrentOperation) + + err = InitializeGasTracking(querier.GetCtx(), &contractMeter) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + prefixStore := prefix.NewStore(gaskv.NewStore(store.Store, &contractMeter, stypes.KVGasConfig()), store.PrefixKey) + + response, vmGasUsed, err := t.vm.Sudo(checksum, env, sudoMsg, NewStoreAdapter(prefixStore), goapi, querier, gasMeter, gasLimit, deserCost) + + updatedGasInfo, trackingErr := t.getActualGas(ctx, CurrentOperation, contractAddress, GasConsumptionInfo{ + SDKGas: 0, + VMGas: vmGasUsed, + }) + if trackingErr != nil { + return response, 0, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + trackingErr = AddVMRecord(*querier.GetCtx(), &VMRecord{ + OriginalVMGas: vmGasUsed, + ActualVMGas: updatedGasInfo.VMGas, + }) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + sessionRecords, trackingErr := TerminateGasTracking(querier.GetCtx()) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + if err != nil && trackingErr == nil { + return response, updatedGasInfo.VMGas, err + } else if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + ingestingErr := t.ingestGasRecords(ctx, sessionRecords) + + if ingestingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: ingestingErr} + } else { + return response, updatedGasInfo.VMGas, nil + } +} + +func (t *TrackingWasmerEngine) Reply(ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store PrefixStoreInfo, goapi wasmvm.GoAPI, querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + const CurrentOperation = ContractOperationReply + var contractAddress = env.Contract.Address + + gasCalcFn, err := t.gasProcessor.GetGasCalculationFn(ctx, contractAddress) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + contractMeter := NewContractGasMeter(gasLimit, gasCalcFn, contractAddress, CurrentOperation) + + err = InitializeGasTracking(querier.GetCtx(), &contractMeter) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + prefixStore := prefix.NewStore(gaskv.NewStore(store.Store, &contractMeter, stypes.KVGasConfig()), store.PrefixKey) + + response, vmGasUsed, err := t.vm.Reply(checksum, env, reply, NewStoreAdapter(prefixStore), goapi, querier, gasMeter, gasLimit, deserCost) + + updatedGasInfo, trackingErr := t.getActualGas(ctx, CurrentOperation, contractAddress, GasConsumptionInfo{ + SDKGas: 0, + VMGas: vmGasUsed, + }) + if trackingErr != nil { + return response, 0, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + trackingErr = AddVMRecord(*querier.GetCtx(), &VMRecord{ + OriginalVMGas: vmGasUsed, + ActualVMGas: updatedGasInfo.VMGas, + }) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + sessionRecords, trackingErr := TerminateGasTracking(querier.GetCtx()) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + if err != nil && trackingErr == nil { + return response, updatedGasInfo.VMGas, err + } else if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + ingestingErr := t.ingestGasRecords(ctx, sessionRecords) + + if ingestingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: ingestingErr} + } else { + return response, updatedGasInfo.VMGas, nil + } +} + +func (t *TrackingWasmerEngine) GetCode(code wasmvm.Checksum) (wasmvm.WasmCode, error) { + return t.vm.GetCode(code) +} + +func (t *TrackingWasmerEngine) Cleanup() { + t.vm.Cleanup() +} + +func (t *TrackingWasmerEngine) IBCChannelOpen(ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelOpenMsg, store PrefixStoreInfo, goapi wasmvm.GoAPI, querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { + const CurrentOperation = ContractOperationIbcChannelOpen + var contractAddress = env.Contract.Address + + gasCalcFn, err := t.gasProcessor.GetGasCalculationFn(ctx, contractAddress) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + contractMeter := NewContractGasMeter(gasLimit, gasCalcFn, contractAddress, CurrentOperation) + + err = InitializeGasTracking(querier.GetCtx(), &contractMeter) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + prefixStore := prefix.NewStore(gaskv.NewStore(store.Store, &contractMeter, stypes.KVGasConfig()), store.PrefixKey) + + response, vmGasUsed, err := t.vm.IBCChannelOpen(checksum, env, channel, NewStoreAdapter(prefixStore), goapi, querier, gasMeter, gasLimit, deserCost) + + updatedGasInfo, trackingErr := t.getActualGas(ctx, CurrentOperation, contractAddress, GasConsumptionInfo{ + SDKGas: 0, + VMGas: vmGasUsed, + }) + if trackingErr != nil { + return response, 0, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + trackingErr = AddVMRecord(*querier.GetCtx(), &VMRecord{ + OriginalVMGas: vmGasUsed, + ActualVMGas: updatedGasInfo.VMGas, + }) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + sessionRecords, trackingErr := TerminateGasTracking(querier.GetCtx()) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + if err != nil && trackingErr == nil { + return response, updatedGasInfo.VMGas, err + } else if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + ingestingErr := t.ingestGasRecords(ctx, sessionRecords) + + if ingestingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: ingestingErr} + } else { + return response, updatedGasInfo.VMGas, nil + } +} + +func (t *TrackingWasmerEngine) IBCChannelConnect(ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelConnectMsg, store PrefixStoreInfo, goapi wasmvm.GoAPI, querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { + const CurrentOperation = ContractOperationIbcChannelConnect + var contractAddress = env.Contract.Address + + gasCalcFn, err := t.gasProcessor.GetGasCalculationFn(ctx, contractAddress) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + contractMeter := NewContractGasMeter(gasLimit, gasCalcFn, contractAddress, CurrentOperation) + + err = InitializeGasTracking(querier.GetCtx(), &contractMeter) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + prefixStore := prefix.NewStore(gaskv.NewStore(store.Store, &contractMeter, stypes.KVGasConfig()), store.PrefixKey) + + response, vmGasUsed, err := t.vm.IBCChannelConnect(checksum, env, channel, NewStoreAdapter(prefixStore), goapi, querier, gasMeter, gasLimit, deserCost) + + updatedGasInfo, trackingErr := t.getActualGas(ctx, CurrentOperation, contractAddress, GasConsumptionInfo{ + SDKGas: 0, + VMGas: vmGasUsed, + }) + if trackingErr != nil { + return response, 0, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + trackingErr = AddVMRecord(*querier.GetCtx(), &VMRecord{ + OriginalVMGas: vmGasUsed, + ActualVMGas: updatedGasInfo.VMGas, + }) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + sessionRecords, trackingErr := TerminateGasTracking(querier.GetCtx()) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + if err != nil && trackingErr == nil { + return response, updatedGasInfo.VMGas, err + } else if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + ingestingErr := t.ingestGasRecords(ctx, sessionRecords) + + if ingestingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: ingestingErr} + } else { + return response, updatedGasInfo.VMGas, nil + } +} + +func (t *TrackingWasmerEngine) IBCChannelClose(ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelCloseMsg, store PrefixStoreInfo, goapi wasmvm.GoAPI, querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { + const CurrentOperation = ContractOperationIbcChannelClose + var contractAddress = env.Contract.Address + + gasCalcFn, err := t.gasProcessor.GetGasCalculationFn(ctx, contractAddress) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + contractMeter := NewContractGasMeter(gasLimit, gasCalcFn, contractAddress, CurrentOperation) + + err = InitializeGasTracking(querier.GetCtx(), &contractMeter) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + prefixStore := prefix.NewStore(gaskv.NewStore(store.Store, &contractMeter, stypes.KVGasConfig()), store.PrefixKey) + + response, vmGasUsed, err := t.vm.IBCChannelClose(checksum, env, channel, NewStoreAdapter(prefixStore), goapi, querier, gasMeter, gasLimit, deserCost) + + updatedGasInfo, trackingErr := t.getActualGas(ctx, CurrentOperation, contractAddress, GasConsumptionInfo{ + SDKGas: 0, + VMGas: vmGasUsed, + }) + if trackingErr != nil { + return response, 0, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + trackingErr = AddVMRecord(*querier.GetCtx(), &VMRecord{ + OriginalVMGas: vmGasUsed, + ActualVMGas: updatedGasInfo.VMGas, + }) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + sessionRecords, trackingErr := TerminateGasTracking(querier.GetCtx()) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + if err != nil && trackingErr == nil { + return response, updatedGasInfo.VMGas, err + } else if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + ingestingErr := t.ingestGasRecords(ctx, sessionRecords) + + if ingestingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: ingestingErr} + } else { + return response, updatedGasInfo.VMGas, nil + } +} + +func (t *TrackingWasmerEngine) IBCPacketReceive(ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, packet wasmvmtypes.IBCPacketReceiveMsg, store PrefixStoreInfo, goapi wasmvm.GoAPI, querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { + const CurrentOperation = ContractOperationIbcPacketReceive + var contractAddress = env.Contract.Address + + gasCalcFn, err := t.gasProcessor.GetGasCalculationFn(ctx, contractAddress) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + contractMeter := NewContractGasMeter(gasLimit, gasCalcFn, contractAddress, CurrentOperation) + + err = InitializeGasTracking(querier.GetCtx(), &contractMeter) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + prefixStore := prefix.NewStore(gaskv.NewStore(store.Store, &contractMeter, stypes.KVGasConfig()), store.PrefixKey) + + response, vmGasUsed, err := t.vm.IBCPacketReceive(checksum, env, packet, NewStoreAdapter(prefixStore), goapi, querier, gasMeter, gasLimit, deserCost) + + updatedGasInfo, trackingErr := t.getActualGas(ctx, CurrentOperation, contractAddress, GasConsumptionInfo{ + SDKGas: 0, + VMGas: vmGasUsed, + }) + if trackingErr != nil { + return response, 0, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + trackingErr = AddVMRecord(*querier.GetCtx(), &VMRecord{ + OriginalVMGas: vmGasUsed, + ActualVMGas: updatedGasInfo.VMGas, + }) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + sessionRecords, trackingErr := TerminateGasTracking(querier.GetCtx()) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + if err != nil && trackingErr == nil { + return response, updatedGasInfo.VMGas, err + } else if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + ingestingErr := t.ingestGasRecords(ctx, sessionRecords) + + if ingestingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: ingestingErr} + } else { + return response, updatedGasInfo.VMGas, nil + } +} + +func (t *TrackingWasmerEngine) IBCPacketAck(ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, ack wasmvmtypes.IBCPacketAckMsg, store PrefixStoreInfo, goapi wasmvm.GoAPI, querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { + const CurrentOperation = ContractOperationIbcPacketAck + var contractAddress = env.Contract.Address + + gasCalcFn, err := t.gasProcessor.GetGasCalculationFn(ctx, contractAddress) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + contractMeter := NewContractGasMeter(gasLimit, gasCalcFn, contractAddress, CurrentOperation) + + err = InitializeGasTracking(querier.GetCtx(), &contractMeter) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + prefixStore := prefix.NewStore(gaskv.NewStore(store.Store, &contractMeter, stypes.KVGasConfig()), store.PrefixKey) + + response, vmGasUsed, err := t.vm.IBCPacketAck(checksum, env, ack, NewStoreAdapter(prefixStore), goapi, querier, gasMeter, gasLimit, deserCost) + + updatedGasInfo, trackingErr := t.getActualGas(ctx, CurrentOperation, contractAddress, GasConsumptionInfo{ + SDKGas: 0, + VMGas: vmGasUsed, + }) + if trackingErr != nil { + return response, 0, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + trackingErr = AddVMRecord(*querier.GetCtx(), &VMRecord{ + OriginalVMGas: vmGasUsed, + ActualVMGas: updatedGasInfo.VMGas, + }) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + sessionRecords, trackingErr := TerminateGasTracking(querier.GetCtx()) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + if err != nil && trackingErr == nil { + return response, updatedGasInfo.VMGas, err + } else if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + ingestingErr := t.ingestGasRecords(ctx, sessionRecords) + + if ingestingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: ingestingErr} + } else { + return response, updatedGasInfo.VMGas, nil + } +} + +func (t *TrackingWasmerEngine) IBCPacketTimeout(ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, packet wasmvmtypes.IBCPacketTimeoutMsg, store PrefixStoreInfo, goapi wasmvm.GoAPI, querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { + const CurrentOperation = ContractOperationIbcPacketTimeout + var contractAddress = env.Contract.Address + + gasCalcFn, err := t.gasProcessor.GetGasCalculationFn(ctx, contractAddress) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + contractMeter := NewContractGasMeter(gasLimit, gasCalcFn, contractAddress, CurrentOperation) + + err = InitializeGasTracking(querier.GetCtx(), &contractMeter) + if err != nil { + return nil, 0, &TrackingVMError{GasProcessorError: err, VmError: nil} + } + + prefixStore := prefix.NewStore(gaskv.NewStore(store.Store, &contractMeter, stypes.KVGasConfig()), store.PrefixKey) + + response, vmGasUsed, err := t.vm.IBCPacketTimeout(checksum, env, packet, NewStoreAdapter(prefixStore), goapi, querier, gasMeter, gasLimit, deserCost) + + updatedGasInfo, trackingErr := t.getActualGas(ctx, CurrentOperation, contractAddress, GasConsumptionInfo{ + SDKGas: 0, + VMGas: vmGasUsed, + }) + if trackingErr != nil { + return response, 0, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + trackingErr = AddVMRecord(*querier.GetCtx(), &VMRecord{ + OriginalVMGas: vmGasUsed, + ActualVMGas: updatedGasInfo.VMGas, + }) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + sessionRecords, trackingErr := TerminateGasTracking(querier.GetCtx()) + if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + if err != nil && trackingErr == nil { + return response, updatedGasInfo.VMGas, err + } else if trackingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: trackingErr} + } + + ingestingErr := t.ingestGasRecords(ctx, sessionRecords) + + if ingestingErr != nil { + return response, updatedGasInfo.VMGas, &TrackingVMError{VmError: err, GasProcessorError: ingestingErr} + } else { + return response, updatedGasInfo.VMGas, nil + } +} + +func (t *TrackingWasmerEngine) Pin(checksum wasmvm.Checksum) error { + return t.vm.Pin(checksum) +} + +func (t *TrackingWasmerEngine) Unpin(checksum wasmvm.Checksum) error { + return t.vm.Unpin(checksum) +} + +func (t *TrackingWasmerEngine) GetMetrics() (*wasmvmtypes.Metrics, error) { + return t.vm.GetMetrics() +} + +var _ WasmEngine = &TrackingWasmerEngine{} diff --git a/x/wasm/types/tracking_wasmer_engine_test.go b/x/wasm/types/tracking_wasmer_engine_test.go new file mode 100644 index 0000000000..f60bf5df90 --- /dev/null +++ b/x/wasm/types/tracking_wasmer_engine_test.go @@ -0,0 +1,1423 @@ +package types + +import ( + "fmt" + "math/rand" + "testing" + + cosmwasm "github.com/CosmWasm/wasmvm" + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + db "github.com/cometbft/cometbft-db" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/store" + stTypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +type testError struct{} + +func (t *testError) Error() string { + return "Fail" +} + +var errTestFail = &testError{} + +type loggingVMLog struct { + MethodName string + Message []byte +} + +type loggingVMLogs []loggingVMLog + +var _ QuerierWithCtx = &testQuerier{} +var _ BareWasmVM = &loggingVM{} +var _ ContractGasProcessor = &testGasProcessor{} + +type testGasProcessor struct { + ingestedRecords []ContractGasRecord +} + +func (t *testGasProcessor) CalculateUpdatedGas(ctx sdk.Context, record ContractGasRecord) (GasConsumptionInfo, error) { + return record.OriginalGas, nil +} + +func (t *testGasProcessor) GetGasCalculationFn(ctx sdk.Context, contractAddress string) (func(operationId uint64, gasInfo GasConsumptionInfo) GasConsumptionInfo, error) { + return func(operationId uint64, gasInfo GasConsumptionInfo) GasConsumptionInfo { + return gasInfo + }, nil +} + +func (t *testGasProcessor) IngestGasRecord(ctx sdk.Context, records []ContractGasRecord) error { + t.ingestedRecords = append(t.ingestedRecords, records...) + return nil +} + +type testQuerier struct { + Ctx sdk.Context + Vm WasmEngine + GasUsed []uint64 + TotalGasUsed uint64 +} + +func (t *testQuerier) Query(request wasmvmtypes.QueryRequest, gasLimit uint64) ([]byte, error) { + t.Ctx, _ = t.Ctx.CacheContext() + if err := CreateNewSession(&t.Ctx, gasLimit); err != nil { + return nil, err + } + response, gasUsed, err := t.Vm.Query( + t.Ctx, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: request.Wasm.Raw.ContractAddr}}, + []byte{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + t, + sdk.NewInfiniteGasMeter(), + gasLimit, + wasmvmtypes.UFraction{}, + ) + if err := DestroySession(&t.Ctx); err != nil { + return nil, err + } + t.GasUsed = append(t.GasUsed, gasUsed) + t.TotalGasUsed += gasUsed + return response, err +} + +func (t *testQuerier) GasConsumed() uint64 { + return t.TotalGasUsed +} + +func (t *testQuerier) GetCtx() *sdk.Context { + return &t.Ctx +} + +type loggingVM struct { + logs loggingVMLogs + GasUsed []uint64 + Fail bool + ShouldEmulateQuery bool + QueryGasUsage uint64 + QueryContracts []string +} + +func (l *loggingVM) Create(code cosmwasm.WasmCode) (cosmwasm.Checksum, error) { + panic("Deprecated: use StoreCode instead") +} + +func (l *loggingVM) StoreCode(code cosmwasm.WasmCode) (cosmwasm.Checksum, error) { + if l.Fail { + return cosmwasm.Checksum{}, errTestFail + } + l.logs = append(l.logs, loggingVMLog{ + MethodName: "StoreCode", + Message: nil, + }) + return cosmwasm.Checksum{}, nil +} + +func (l *loggingVM) StoreCodeUnchecked(code cosmwasm.WasmCode) (cosmwasm.Checksum, error) { + if l.Fail { + return cosmwasm.Checksum{}, errTestFail + } + l.logs = append(l.logs, loggingVMLog{ + MethodName: "StoreCodeUnchecked", + Message: nil, + }) + return cosmwasm.Checksum{}, nil +} + +func (l *loggingVM) AnalyzeCode(checksum cosmwasm.Checksum) (*wasmvmtypes.AnalysisReport, error) { + if l.Fail { + return nil, errTestFail + } + l.logs = append(l.logs, loggingVMLog{ + MethodName: "AnalyzeCode", + Message: nil, + }) + return nil, nil +} + +func (l *loggingVM) Instantiate(checksum cosmwasm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + if l.Fail { + return &wasmvmtypes.Response{}, 0, errTestFail + } + if l.ShouldEmulateQuery && len(l.QueryContracts) > 0 { + querier.Query(wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{Raw: &wasmvmtypes.RawQuery{ContractAddr: l.QueryContracts[0]}}}, uint64(len(l.QueryContracts))-1) + } + currentOperationGas := rand.Uint64() % 50000 + l.GasUsed = append(l.GasUsed, currentOperationGas) + l.logs = append(l.logs, loggingVMLog{ + MethodName: "Instantiate", + Message: initMsg, + }) + return &wasmvmtypes.Response{}, currentOperationGas, nil +} + +func (l *loggingVM) Execute(code cosmwasm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + if l.Fail { + return &wasmvmtypes.Response{}, 0, errTestFail + } + if l.ShouldEmulateQuery && len(l.QueryContracts) > 0 { + querier.Query(wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{Raw: &wasmvmtypes.RawQuery{ContractAddr: l.QueryContracts[0]}}}, uint64(len(l.QueryContracts))-1) + } + currentOperationGas := rand.Uint64() % 50000 + l.GasUsed = append(l.GasUsed, currentOperationGas) + l.logs = append(l.logs, loggingVMLog{ + MethodName: "Execute", + Message: executeMsg, + }) + return &wasmvmtypes.Response{}, currentOperationGas, nil +} + +func (l *loggingVM) Query(code cosmwasm.Checksum, env wasmvmtypes.Env, queryMsg []byte, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) ([]byte, uint64, error) { + if l.Fail { + return []byte{}, 0, errTestFail + } + if l.ShouldEmulateQuery && gasLimit >= 1 { + querier.Query(wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{Raw: &wasmvmtypes.RawQuery{ContractAddr: l.QueryContracts[uint64(len(l.QueryContracts))-gasLimit]}}}, gasLimit-1) + } + currentOperationGas := rand.Uint64() % 50000 + l.GasUsed = append(l.GasUsed, currentOperationGas) + l.logs = append(l.logs, loggingVMLog{ + MethodName: "Query", + Message: queryMsg, + }) + return []byte{1}, currentOperationGas, nil +} + +func (l *loggingVM) Migrate(checksum cosmwasm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + if l.Fail { + return &wasmvmtypes.Response{}, 0, errTestFail + } + if l.ShouldEmulateQuery && len(l.QueryContracts) > 0 { + querier.Query(wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{Raw: &wasmvmtypes.RawQuery{ContractAddr: l.QueryContracts[0]}}}, uint64(len(l.QueryContracts))-1) + } + currentOperationGas := rand.Uint64() % 50000 + l.GasUsed = append(l.GasUsed, currentOperationGas) + l.logs = append(l.logs, loggingVMLog{ + MethodName: "Migrate", + Message: migrateMsg, + }) + return &wasmvmtypes.Response{}, currentOperationGas, nil +} + +func (l *loggingVM) Sudo(checksum cosmwasm.Checksum, env wasmvmtypes.Env, sudoMsg []byte, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + if l.Fail { + return &wasmvmtypes.Response{}, 0, errTestFail + } + if l.ShouldEmulateQuery && len(l.QueryContracts) > 0 { + querier.Query(wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{Raw: &wasmvmtypes.RawQuery{ContractAddr: l.QueryContracts[0]}}}, uint64(len(l.QueryContracts))-1) + } + currentOperationGas := rand.Uint64() % 50000 + l.GasUsed = append(l.GasUsed, currentOperationGas) + l.logs = append(l.logs, loggingVMLog{ + MethodName: "Sudo", + Message: sudoMsg, + }) + return &wasmvmtypes.Response{}, currentOperationGas, nil +} + +func (l *loggingVM) Reply(checksum cosmwasm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.Response, uint64, error) { + if l.Fail { + return &wasmvmtypes.Response{}, 0, errTestFail + } + if l.ShouldEmulateQuery && len(l.QueryContracts) > 0 { + querier.Query(wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{Raw: &wasmvmtypes.RawQuery{ContractAddr: l.QueryContracts[0]}}}, uint64(len(l.QueryContracts))-1) + } + currentOperationGas := rand.Uint64() % 50000 + l.GasUsed = append(l.GasUsed, currentOperationGas) + l.logs = append(l.logs, loggingVMLog{ + MethodName: "Reply", + Message: nil, + }) + return &wasmvmtypes.Response{}, currentOperationGas, nil +} + +func (l *loggingVM) GetCode(code cosmwasm.Checksum) (cosmwasm.WasmCode, error) { + panic("not implemented in test") +} + +func (l *loggingVM) Cleanup() { + panic("not implemented in test") +} + +func (l *loggingVM) IBCChannelOpen(checksum cosmwasm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelOpenMsg, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBC3ChannelOpenResponse, uint64, error) { + if l.Fail { + return nil, 0, errTestFail + } + if l.ShouldEmulateQuery && len(l.QueryContracts) > 0 { + querier.Query(wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{Raw: &wasmvmtypes.RawQuery{ContractAddr: l.QueryContracts[0]}}}, uint64(len(l.QueryContracts))-1) + } + currentOperationGas := rand.Uint64() % 50000 + l.GasUsed = append(l.GasUsed, currentOperationGas) + l.logs = append(l.logs, loggingVMLog{ + MethodName: "IBCChannelOpen", + Message: nil, + }) + return &wasmvmtypes.IBC3ChannelOpenResponse{}, currentOperationGas, nil +} + +func (l *loggingVM) IBCChannelConnect(checksum cosmwasm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelConnectMsg, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { + if l.Fail { + return &wasmvmtypes.IBCBasicResponse{}, 0, errTestFail + } + if l.ShouldEmulateQuery && len(l.QueryContracts) > 0 { + querier.Query(wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{Raw: &wasmvmtypes.RawQuery{ContractAddr: l.QueryContracts[0]}}}, uint64(len(l.QueryContracts))-1) + } + currentOperationGas := rand.Uint64() % 50000 + l.GasUsed = append(l.GasUsed, currentOperationGas) + l.logs = append(l.logs, loggingVMLog{ + MethodName: "IBCChannelConnect", + Message: nil, + }) + return &wasmvmtypes.IBCBasicResponse{}, currentOperationGas, nil +} + +func (l *loggingVM) IBCChannelClose(checksum cosmwasm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelCloseMsg, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { + if l.Fail { + return &wasmvmtypes.IBCBasicResponse{}, 0, errTestFail + } + if l.ShouldEmulateQuery && len(l.QueryContracts) > 0 { + querier.Query(wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{Raw: &wasmvmtypes.RawQuery{ContractAddr: l.QueryContracts[0]}}}, uint64(len(l.QueryContracts))-1) + } + currentOperationGas := rand.Uint64() % 50000 + l.GasUsed = append(l.GasUsed, currentOperationGas) + l.logs = append(l.logs, loggingVMLog{ + MethodName: "IBCChannelClose", + Message: nil, + }) + return &wasmvmtypes.IBCBasicResponse{}, currentOperationGas, nil +} + +func (l *loggingVM) IBCPacketReceive(checksum cosmwasm.Checksum, env wasmvmtypes.Env, packet wasmvmtypes.IBCPacketReceiveMsg, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCReceiveResult, uint64, error) { + if l.Fail { + return &wasmvmtypes.IBCReceiveResult{}, 0, errTestFail + } + if l.ShouldEmulateQuery && len(l.QueryContracts) > 0 { + querier.Query(wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{Raw: &wasmvmtypes.RawQuery{ContractAddr: l.QueryContracts[0]}}}, uint64(len(l.QueryContracts))-1) + } + currentOperationGas := rand.Uint64() % 50000 + l.GasUsed = append(l.GasUsed, currentOperationGas) + l.logs = append(l.logs, loggingVMLog{ + MethodName: "IBCPacketReceive", + Message: nil, + }) + return &wasmvmtypes.IBCReceiveResult{}, currentOperationGas, nil +} + +func (l *loggingVM) IBCPacketAck(checksum cosmwasm.Checksum, env wasmvmtypes.Env, ack wasmvmtypes.IBCPacketAckMsg, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { + if l.Fail { + return &wasmvmtypes.IBCBasicResponse{}, 0, errTestFail + } + if l.ShouldEmulateQuery && len(l.QueryContracts) > 0 { + querier.Query(wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{Raw: &wasmvmtypes.RawQuery{ContractAddr: l.QueryContracts[0]}}}, uint64(len(l.QueryContracts))-1) + } + currentOperationGas := rand.Uint64() % 50000 + l.GasUsed = append(l.GasUsed, currentOperationGas) + l.logs = append(l.logs, loggingVMLog{ + MethodName: "IBCPacketAck", + Message: nil, + }) + return &wasmvmtypes.IBCBasicResponse{}, currentOperationGas, nil +} + +func (l *loggingVM) IBCPacketTimeout(checksum cosmwasm.Checksum, env wasmvmtypes.Env, packet wasmvmtypes.IBCPacketTimeoutMsg, store cosmwasm.KVStore, goapi cosmwasm.GoAPI, querier cosmwasm.Querier, gasMeter cosmwasm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction) (*wasmvmtypes.IBCBasicResponse, uint64, error) { + if l.Fail { + return &wasmvmtypes.IBCBasicResponse{}, 0, errTestFail + } + if l.ShouldEmulateQuery && len(l.QueryContracts) > 0 { + querier.Query(wasmvmtypes.QueryRequest{Wasm: &wasmvmtypes.WasmQuery{Raw: &wasmvmtypes.RawQuery{ContractAddr: l.QueryContracts[0]}}}, uint64(len(l.QueryContracts))-1) + } + currentOperationGas := rand.Uint64() % 50000 + l.GasUsed = append(l.GasUsed, currentOperationGas) + l.logs = append(l.logs, loggingVMLog{ + MethodName: "IBCPacketTimeout", + Message: nil, + }) + return &wasmvmtypes.IBCBasicResponse{}, currentOperationGas, nil +} + +func (l *loggingVM) Pin(checksum cosmwasm.Checksum) error { + if l.Fail { + return errTestFail + } + l.logs = append(l.logs, loggingVMLog{ + MethodName: "Pin", + Message: nil, + }) + return nil +} + +func (l *loggingVM) Unpin(checksum cosmwasm.Checksum) error { + if l.Fail { + return errTestFail + } + l.logs = append(l.logs, loggingVMLog{ + MethodName: "Unpin", + Message: nil, + }) + return nil +} + +func (l *loggingVM) GetMetrics() (*wasmvmtypes.Metrics, error) { + if l.Fail { + return nil, errTestFail + } + l.logs = append(l.logs, loggingVMLog{ + MethodName: "GetMetrics", + Message: nil, + }) + return nil, nil +} + +func (l *loggingVM) Reset() { + l.logs = nil + l.Fail = false + l.GasUsed = nil +} + +func TestGasTrackingVMInstantiateAndQuery(t *testing.T) { + memDB := db.NewMemDB() + cms := store.NewCommitMultiStore(memDB) + emptyContext := sdk.NewContext(cms, tmproto.Header{}, false, nil) + + loggingVM := loggingVM{ + Fail: false, + ShouldEmulateQuery: true, + QueryGasUsage: 50, + QueryContracts: []string{"1contract", "2contract", "3contract", "4contract"}, + } + + testGasRecorder := &testGasProcessor{} + gasTrackingVm := TrackingWasmerEngine{vm: &loggingVM, gasProcessor: testGasRecorder} + + testQuerier := testQuerier{ + Ctx: emptyContext, + Vm: &gasTrackingVm, + } + testQuerier.Ctx = emptyContext + + _, gasUsed, err := gasTrackingVm.Instantiate( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.MessageInfo{}, + []byte{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Instantiation should succeed") + + require.Equal(t, 5, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 4, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + for i, record := range testQuerier.GasUsed { + require.Equal(t, loggingVM.GasUsed[i], record) + require.Equal(t, testGasRecorder.ingestedRecords[i].OriginalGas.VMGas, record, "Ingested record's gas consumed must match querier's record") + require.Equal(t, testGasRecorder.ingestedRecords[i].OperationId, ContractOperationQuery, "Operation must be query") + require.Equal(t, testGasRecorder.ingestedRecords[i].ContractAddress, loggingVM.QueryContracts[(len(loggingVM.QueryContracts)-1)-i], "Contract address must be correct") + } + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationInstantiate, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + loggingVM.Reset() + testQuerier.GasUsed = nil + testQuerier.Ctx = emptyContext + testGasRecorder.ingestedRecords = nil + + _, gasUsed, err = gasTrackingVm.Query( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + []byte{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 4, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Query should succeed") + + require.Nil(t, testGasRecorder.ingestedRecords, "Ingested gas records should be nil") + + require.Equal(t, 5, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 4, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + for i, record := range testQuerier.GasUsed { + require.Equal(t, loggingVM.GasUsed[i], record) + require.Equal(t, record, loggingVM.GasUsed[i], "Ingested record's gas consumed must match querier's record") + } + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") + + loggingVM.Reset() + testQuerier.GasUsed = nil + testQuerier.Ctx = emptyContext + testGasRecorder.ingestedRecords = nil + + loggingVM.ShouldEmulateQuery = false + + _, gasUsed, err = gasTrackingVm.Instantiate( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.MessageInfo{}, + []byte{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Query should succeed") + + require.Equal(t, 1, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 0, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + require.Equal(t, 1, len(testGasRecorder.ingestedRecords), "There should be proper number of gas records") + + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationInstantiate, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") + + loggingVM.Reset() + testQuerier.GasUsed = nil + testQuerier.Ctx = emptyContext + testGasRecorder.ingestedRecords = nil + + _, gasUsed, err = gasTrackingVm.Query( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + []byte{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 0, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Query should succeed") + + require.Nil(t, testGasRecorder.ingestedRecords, "Ingested gas records should be nil") + + require.Equal(t, 1, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 0, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") +} + +func TestGasTrackingVMExecute(t *testing.T) { + memDB := db.NewMemDB() + cms := store.NewCommitMultiStore(memDB) + emptyContext := sdk.NewContext(cms, tmproto.Header{}, false, nil) + + loggingVM := loggingVM{ + Fail: false, + ShouldEmulateQuery: true, + QueryGasUsage: 50, + QueryContracts: []string{"1contract", "2contract", "3contract", "4contract"}, + } + + testGasRecorder := &testGasProcessor{} + gasTrackingVm := TrackingWasmerEngine{vm: &loggingVM, gasProcessor: testGasRecorder} + + testQuerier := testQuerier{ + Ctx: emptyContext, + Vm: &gasTrackingVm, + } + testQuerier.Ctx = emptyContext + + _, gasUsed, err := gasTrackingVm.Execute( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.MessageInfo{}, + []byte{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Instantiation should succeed") + + require.Equal(t, 5, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 4, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + for i, record := range testQuerier.GasUsed { + require.Equal(t, loggingVM.GasUsed[i], record) + require.Equal(t, testGasRecorder.ingestedRecords[i].OriginalGas.VMGas, record, "Ingested record's gas consumed must match querier's record") + require.Equal(t, testGasRecorder.ingestedRecords[i].OperationId, ContractOperationQuery, "Operation must be query") + require.Equal(t, testGasRecorder.ingestedRecords[i].ContractAddress, loggingVM.QueryContracts[(len(loggingVM.QueryContracts)-1)-i], "Contract address must be correct") + } + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationExecute, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + loggingVM.Reset() + testQuerier.GasUsed = nil + testQuerier.Ctx = emptyContext + testGasRecorder.ingestedRecords = nil + + loggingVM.ShouldEmulateQuery = false + + _, gasUsed, err = gasTrackingVm.Execute( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.MessageInfo{}, + []byte{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Query should succeed") + + require.Equal(t, 1, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 0, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + require.Equal(t, 1, len(testGasRecorder.ingestedRecords), "There should be proper number of gas records") + + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationExecute, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") +} + +func TestGasTrackingVMMigrate(t *testing.T) { + memDB := db.NewMemDB() + cms := store.NewCommitMultiStore(memDB) + emptyContext := sdk.NewContext(cms, tmproto.Header{}, false, nil) + + loggingVM := loggingVM{ + Fail: false, + ShouldEmulateQuery: true, + QueryGasUsage: 50, + QueryContracts: []string{"1contract", "2contract", "3contract", "4contract"}, + } + + testGasRecorder := &testGasProcessor{} + gasTrackingVm := TrackingWasmerEngine{vm: &loggingVM, gasProcessor: testGasRecorder} + + testQuerier := testQuerier{ + Ctx: emptyContext, + Vm: &gasTrackingVm, + } + testQuerier.Ctx = emptyContext + + _, gasUsed, err := gasTrackingVm.Migrate( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + []byte{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Instantiation should succeed") + + require.Equal(t, 5, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 4, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + for i, record := range testQuerier.GasUsed { + require.Equal(t, loggingVM.GasUsed[i], record) + require.Equal(t, testGasRecorder.ingestedRecords[i].OriginalGas.VMGas, record, "Ingested record's gas consumed must match querier's record") + require.Equal(t, testGasRecorder.ingestedRecords[i].OperationId, ContractOperationQuery, "Operation must be query") + require.Equal(t, testGasRecorder.ingestedRecords[i].ContractAddress, loggingVM.QueryContracts[(len(loggingVM.QueryContracts)-1)-i], "Contract address must be correct") + } + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationMigrate, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + loggingVM.Reset() + testQuerier.GasUsed = nil + testQuerier.Ctx = emptyContext + testGasRecorder.ingestedRecords = nil + + loggingVM.ShouldEmulateQuery = false + + _, gasUsed, err = gasTrackingVm.Migrate( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + []byte{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Query should succeed") + + require.Equal(t, 1, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 0, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + require.Equal(t, 1, len(testGasRecorder.ingestedRecords), "There should be proper number of gas records") + + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationMigrate, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") +} + +func TestGasTrackingVMSudo(t *testing.T) { + memDB := db.NewMemDB() + cms := store.NewCommitMultiStore(memDB) + emptyContext := sdk.NewContext(cms, tmproto.Header{}, false, nil) + + loggingVM := loggingVM{ + Fail: false, + ShouldEmulateQuery: true, + QueryGasUsage: 50, + QueryContracts: []string{"1contract", "2contract", "3contract", "4contract"}, + } + + testGasRecorder := &testGasProcessor{} + gasTrackingVm := TrackingWasmerEngine{vm: &loggingVM, gasProcessor: testGasRecorder} + + testQuerier := testQuerier{ + Ctx: emptyContext, + Vm: &gasTrackingVm, + } + testQuerier.Ctx = emptyContext + + _, gasUsed, err := gasTrackingVm.Sudo( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + []byte{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Instantiation should succeed") + + fmt.Println(testGasRecorder.ingestedRecords, loggingVM.GasUsed, testQuerier.GasUsed) + + require.Equal(t, 5, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 4, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + for i, record := range testQuerier.GasUsed { + require.Equal(t, loggingVM.GasUsed[i], record) + require.Equal(t, testGasRecorder.ingestedRecords[i].OriginalGas.VMGas, record, "Ingested record's gas consumed must match querier's record") + require.Equal(t, testGasRecorder.ingestedRecords[i].OperationId, ContractOperationQuery, "Operation must be query") + require.Equal(t, testGasRecorder.ingestedRecords[i].ContractAddress, loggingVM.QueryContracts[(len(loggingVM.QueryContracts)-1)-i], "Contract address must be correct") + } + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationSudo, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + loggingVM.Reset() + testQuerier.GasUsed = nil + testQuerier.Ctx = emptyContext + testGasRecorder.ingestedRecords = nil + + loggingVM.ShouldEmulateQuery = false + + _, gasUsed, err = gasTrackingVm.Sudo( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + []byte{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Query should succeed") + + require.Equal(t, 1, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 0, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + require.Equal(t, 1, len(testGasRecorder.ingestedRecords), "There should be proper number of gas records") + + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationSudo, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") +} + +func TestGasTrackingVMReply(t *testing.T) { + memDB := db.NewMemDB() + cms := store.NewCommitMultiStore(memDB) + emptyContext := sdk.NewContext(cms, tmproto.Header{}, false, nil) + + loggingVM := loggingVM{ + Fail: false, + ShouldEmulateQuery: true, + QueryGasUsage: 50, + QueryContracts: []string{"1contract", "2contract", "3contract", "4contract"}, + } + + testGasRecorder := &testGasProcessor{} + gasTrackingVm := TrackingWasmerEngine{vm: &loggingVM, gasProcessor: testGasRecorder} + + testQuerier := testQuerier{ + Ctx: emptyContext, + Vm: &gasTrackingVm, + } + testQuerier.Ctx = emptyContext + + _, gasUsed, err := gasTrackingVm.Reply( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.Reply{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Instantiation should succeed") + + require.Equal(t, 5, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 4, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + for i, record := range testQuerier.GasUsed { + require.Equal(t, loggingVM.GasUsed[i], record) + require.Equal(t, testGasRecorder.ingestedRecords[i].OriginalGas.VMGas, record, "Ingested record's gas consumed must match querier's record") + require.Equal(t, testGasRecorder.ingestedRecords[i].OperationId, ContractOperationQuery, "Operation must be query") + require.Equal(t, testGasRecorder.ingestedRecords[i].ContractAddress, loggingVM.QueryContracts[(len(loggingVM.QueryContracts)-1)-i], "Contract address must be correct") + } + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationReply, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + loggingVM.Reset() + testQuerier.GasUsed = nil + testQuerier.Ctx = emptyContext + testGasRecorder.ingestedRecords = nil + + loggingVM.ShouldEmulateQuery = false + + _, gasUsed, err = gasTrackingVm.Reply( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.Reply{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Query should succeed") + + require.Equal(t, 1, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 0, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + require.Equal(t, 1, len(testGasRecorder.ingestedRecords), "There should be proper number of gas records") + + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationReply, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") +} + +func TestGasTrackingVMIBCChannelOpen(t *testing.T) { + memDB := db.NewMemDB() + cms := store.NewCommitMultiStore(memDB) + emptyContext := sdk.NewContext(cms, tmproto.Header{}, false, nil) + + loggingVM := loggingVM{ + Fail: false, + ShouldEmulateQuery: true, + QueryGasUsage: 50, + QueryContracts: []string{"1contract", "2contract", "3contract", "4contract"}, + } + + testGasRecorder := &testGasProcessor{} + gasTrackingVm := TrackingWasmerEngine{vm: &loggingVM, gasProcessor: testGasRecorder} + + testQuerier := testQuerier{ + Ctx: emptyContext, + Vm: &gasTrackingVm, + } + testQuerier.Ctx = emptyContext + + _, gasUsed, err := gasTrackingVm.IBCChannelOpen( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.IBCChannelOpenMsg{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Instantiation should succeed") + + require.Equal(t, 5, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 4, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + for i, record := range testQuerier.GasUsed { + require.Equal(t, loggingVM.GasUsed[i], record) + require.Equal(t, testGasRecorder.ingestedRecords[i].OriginalGas.VMGas, record, "Ingested record's gas consumed must match querier's record") + require.Equal(t, testGasRecorder.ingestedRecords[i].OperationId, ContractOperationQuery, "Operation must be query") + require.Equal(t, testGasRecorder.ingestedRecords[i].ContractAddress, loggingVM.QueryContracts[(len(loggingVM.QueryContracts)-1)-i], "Contract address must be correct") + } + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationIbcChannelOpen, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + loggingVM.Reset() + testQuerier.GasUsed = nil + testQuerier.Ctx = emptyContext + testGasRecorder.ingestedRecords = nil + + loggingVM.ShouldEmulateQuery = false + + _, gasUsed, err = gasTrackingVm.IBCChannelOpen( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.IBCChannelOpenMsg{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Query should succeed") + + require.Equal(t, 1, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 0, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + require.Equal(t, 1, len(testGasRecorder.ingestedRecords), "There should be proper number of gas records") + + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationIbcChannelOpen, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") +} + +func TestGasTrackingVMIBCChannelConnect(t *testing.T) { + memDB := db.NewMemDB() + cms := store.NewCommitMultiStore(memDB) + emptyContext := sdk.NewContext(cms, tmproto.Header{}, false, nil) + + loggingVM := loggingVM{ + Fail: false, + ShouldEmulateQuery: true, + QueryGasUsage: 50, + QueryContracts: []string{"1contract", "2contract", "3contract", "4contract"}, + } + + testGasRecorder := &testGasProcessor{} + gasTrackingVm := TrackingWasmerEngine{vm: &loggingVM, gasProcessor: testGasRecorder} + + testQuerier := testQuerier{ + Ctx: emptyContext, + Vm: &gasTrackingVm, + } + testQuerier.Ctx = emptyContext + + _, gasUsed, err := gasTrackingVm.IBCChannelConnect( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.IBCChannelConnectMsg{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Instantiation should succeed") + + require.Equal(t, 5, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 4, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + for i, record := range testQuerier.GasUsed { + require.Equal(t, loggingVM.GasUsed[i], record) + require.Equal(t, testGasRecorder.ingestedRecords[i].OriginalGas.VMGas, record, "Ingested record's gas consumed must match querier's record") + require.Equal(t, testGasRecorder.ingestedRecords[i].OperationId, ContractOperationQuery, "Operation must be query") + require.Equal(t, testGasRecorder.ingestedRecords[i].ContractAddress, loggingVM.QueryContracts[(len(loggingVM.QueryContracts)-1)-i], "Contract address must be correct") + } + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationIbcChannelConnect, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + loggingVM.Reset() + testQuerier.GasUsed = nil + testQuerier.Ctx = emptyContext + testGasRecorder.ingestedRecords = nil + + loggingVM.ShouldEmulateQuery = false + + _, gasUsed, err = gasTrackingVm.IBCChannelConnect( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.IBCChannelConnectMsg{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Query should succeed") + + require.Equal(t, 1, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 0, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + require.Equal(t, 1, len(testGasRecorder.ingestedRecords), "There should be proper number of gas records") + + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationIbcChannelConnect, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") +} + +func TestGasTrackingVMIBCChannelClose(t *testing.T) { + memDB := db.NewMemDB() + cms := store.NewCommitMultiStore(memDB) + emptyContext := sdk.NewContext(cms, tmproto.Header{}, false, nil) + + loggingVM := loggingVM{ + Fail: false, + ShouldEmulateQuery: true, + QueryGasUsage: 50, + QueryContracts: []string{"1contract", "2contract", "3contract", "4contract"}, + } + + testGasRecorder := &testGasProcessor{} + gasTrackingVm := TrackingWasmerEngine{vm: &loggingVM, gasProcessor: testGasRecorder} + + testQuerier := testQuerier{ + Ctx: emptyContext, + Vm: &gasTrackingVm, + } + testQuerier.Ctx = emptyContext + + _, gasUsed, err := gasTrackingVm.IBCChannelClose( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.IBCChannelCloseMsg{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Instantiation should succeed") + + require.Equal(t, 5, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 4, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + for i, record := range testQuerier.GasUsed { + require.Equal(t, loggingVM.GasUsed[i], record) + require.Equal(t, testGasRecorder.ingestedRecords[i].OriginalGas.VMGas, record, "Ingested record's gas consumed must match querier's record") + require.Equal(t, testGasRecorder.ingestedRecords[i].OperationId, ContractOperationQuery, "Operation must be query") + require.Equal(t, testGasRecorder.ingestedRecords[i].ContractAddress, loggingVM.QueryContracts[(len(loggingVM.QueryContracts)-1)-i], "Contract address must be correct") + } + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationIbcChannelClose, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + loggingVM.Reset() + testQuerier.GasUsed = nil + testQuerier.Ctx = emptyContext + testGasRecorder.ingestedRecords = nil + + loggingVM.ShouldEmulateQuery = false + + _, gasUsed, err = gasTrackingVm.IBCChannelClose( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.IBCChannelCloseMsg{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Query should succeed") + + require.Equal(t, 1, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 0, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + require.Equal(t, 1, len(testGasRecorder.ingestedRecords), "There should be proper number of gas records") + + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationIbcChannelClose, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") +} + +func TestGasTrackingVMIBCPacketReceive(t *testing.T) { + memDB := db.NewMemDB() + cms := store.NewCommitMultiStore(memDB) + emptyContext := sdk.NewContext(cms, tmproto.Header{}, false, nil) + + loggingVM := loggingVM{ + Fail: false, + ShouldEmulateQuery: true, + QueryGasUsage: 50, + QueryContracts: []string{"1contract", "2contract", "3contract", "4contract"}, + } + + testGasRecorder := &testGasProcessor{} + gasTrackingVm := TrackingWasmerEngine{vm: &loggingVM, gasProcessor: testGasRecorder} + + testQuerier := testQuerier{ + Ctx: emptyContext, + Vm: &gasTrackingVm, + } + testQuerier.Ctx = emptyContext + + _, gasUsed, err := gasTrackingVm.IBCPacketReceive( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.IBCPacketReceiveMsg{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Instantiation should succeed") + + require.Equal(t, 5, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 4, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + for i, record := range testQuerier.GasUsed { + require.Equal(t, loggingVM.GasUsed[i], record) + require.Equal(t, testGasRecorder.ingestedRecords[i].OriginalGas.VMGas, record, "Ingested record's gas consumed must match querier's record") + require.Equal(t, testGasRecorder.ingestedRecords[i].OperationId, ContractOperationQuery, "Operation must be query") + require.Equal(t, testGasRecorder.ingestedRecords[i].ContractAddress, loggingVM.QueryContracts[(len(loggingVM.QueryContracts)-1)-i], "Contract address must be correct") + } + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationIbcPacketReceive, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + loggingVM.Reset() + testQuerier.GasUsed = nil + testQuerier.Ctx = emptyContext + testGasRecorder.ingestedRecords = nil + + loggingVM.ShouldEmulateQuery = false + + _, gasUsed, err = gasTrackingVm.IBCPacketReceive( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.IBCPacketReceiveMsg{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Query should succeed") + + require.Equal(t, 1, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 0, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + require.Equal(t, 1, len(testGasRecorder.ingestedRecords), "There should be proper number of gas records") + + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationIbcPacketReceive, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") +} + +func TestGasTrackingVMIBCPacketAck(t *testing.T) { + memDB := db.NewMemDB() + cms := store.NewCommitMultiStore(memDB) + emptyContext := sdk.NewContext(cms, tmproto.Header{}, false, nil) + + loggingVM := loggingVM{ + Fail: false, + ShouldEmulateQuery: true, + QueryGasUsage: 50, + QueryContracts: []string{"1contract", "2contract", "3contract", "4contract"}, + } + + testGasRecorder := &testGasProcessor{} + gasTrackingVm := TrackingWasmerEngine{vm: &loggingVM, gasProcessor: testGasRecorder} + + testQuerier := testQuerier{ + Ctx: emptyContext, + Vm: &gasTrackingVm, + } + testQuerier.Ctx = emptyContext + + _, gasUsed, err := gasTrackingVm.IBCPacketAck( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.IBCPacketAckMsg{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Instantiation should succeed") + + require.Equal(t, 5, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 4, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + for i, record := range testQuerier.GasUsed { + require.Equal(t, loggingVM.GasUsed[i], record) + require.Equal(t, testGasRecorder.ingestedRecords[i].OriginalGas.VMGas, record, "Ingested record's gas consumed must match querier's record") + require.Equal(t, testGasRecorder.ingestedRecords[i].OperationId, ContractOperationQuery, "Operation must be query") + require.Equal(t, testGasRecorder.ingestedRecords[i].ContractAddress, loggingVM.QueryContracts[(len(loggingVM.QueryContracts)-1)-i], "Contract address must be correct") + } + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationIbcPacketAck, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + loggingVM.Reset() + testQuerier.GasUsed = nil + testQuerier.Ctx = emptyContext + testGasRecorder.ingestedRecords = nil + + loggingVM.ShouldEmulateQuery = false + + _, gasUsed, err = gasTrackingVm.IBCPacketAck( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.IBCPacketAckMsg{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Query should succeed") + + require.Equal(t, 1, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 0, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + require.Equal(t, 1, len(testGasRecorder.ingestedRecords), "There should be proper number of gas records") + + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationIbcPacketAck, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") +} + +func TestGasTrackingVMIBCPacketTimeout(t *testing.T) { + memDB := db.NewMemDB() + cms := store.NewCommitMultiStore(memDB) + emptyContext := sdk.NewContext(cms, tmproto.Header{}, false, nil) + + loggingVM := loggingVM{ + Fail: false, + ShouldEmulateQuery: true, + QueryGasUsage: 50, + QueryContracts: []string{"1contract", "2contract", "3contract", "4contract"}, + } + + testGasRecorder := &testGasProcessor{} + gasTrackingVm := TrackingWasmerEngine{vm: &loggingVM, gasProcessor: testGasRecorder} + + testQuerier := testQuerier{ + Ctx: emptyContext, + Vm: &gasTrackingVm, + } + testQuerier.Ctx = emptyContext + + _, gasUsed, err := gasTrackingVm.IBCPacketTimeout( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.IBCPacketTimeoutMsg{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Instantiation should succeed") + + require.Equal(t, 5, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 4, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + for i, record := range testQuerier.GasUsed { + require.Equal(t, loggingVM.GasUsed[i], record) + require.Equal(t, testGasRecorder.ingestedRecords[i].OriginalGas.VMGas, record, "Ingested record's gas consumed must match querier's record") + require.Equal(t, testGasRecorder.ingestedRecords[i].OperationId, ContractOperationQuery, "Operation must be query") + require.Equal(t, testGasRecorder.ingestedRecords[i].ContractAddress, loggingVM.QueryContracts[(len(loggingVM.QueryContracts)-1)-i], "Contract address must be correct") + } + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationIbcPacketTimeout, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + loggingVM.Reset() + testQuerier.GasUsed = nil + testQuerier.Ctx = emptyContext + testGasRecorder.ingestedRecords = nil + + loggingVM.ShouldEmulateQuery = false + + _, gasUsed, err = gasTrackingVm.IBCPacketTimeout( + emptyContext, + cosmwasm.Checksum{}, + wasmvmtypes.Env{Contract: wasmvmtypes.ContractInfo{Address: "1"}}, + wasmvmtypes.IBCPacketTimeoutMsg{}, + PrefixStoreInfo{Store: store.NewCommitMultiStore(db.NewMemDB()).GetCommitKVStore(stTypes.NewKVStoreKey("test")), PrefixKey: []byte{0x1}}, + cosmwasm.GoAPI{}, + &testQuerier, + sdk.NewInfiniteGasMeter(), + 50, + wasmvmtypes.UFraction{}, + ) + + require.NoError(t, err, "Query should succeed") + + require.Equal(t, 1, len(loggingVM.GasUsed), "There should be proper number of records of gas used") + require.Equal(t, 0, len(testQuerier.GasUsed), "There should be proper number of records of query gas") + + require.Equal(t, 1, len(testGasRecorder.ingestedRecords), "There should be proper number of gas records") + + require.Equal(t, ContractGasRecord{ + OperationId: ContractOperationIbcPacketTimeout, + ContractAddress: "1", + OriginalGas: GasConsumptionInfo{VMGas: gasUsed}, + }, testGasRecorder.ingestedRecords[len(testGasRecorder.ingestedRecords)-1], "Last record must be correct") + + require.Equal(t, gasUsed, loggingVM.GasUsed[len(loggingVM.GasUsed)-1], "GasUsed received on response should be same as loggingVm's logs") +} diff --git a/x/wasm/types/wasmer_engine.go b/x/wasm/types/wasmer_engine.go index d3b465de77..84dd18201a 100644 --- a/x/wasm/types/wasmer_engine.go +++ b/x/wasm/types/wasmer_engine.go @@ -4,12 +4,23 @@ import ( wasmvm "github.com/CosmWasm/wasmvm" wasmvmtypes "github.com/CosmWasm/wasmvm/types" + "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" ) // DefaultMaxQueryStackSize maximum size of the stack of contract instances doing queries const DefaultMaxQueryStackSize uint32 = 10 +type QuerierWithCtx interface { + wasmvm.Querier + GetCtx() *sdk.Context +} + +type PrefixStoreInfo struct { + Store store.KVStore + PrefixKey []byte +} + // WasmEngine defines the WASM contract runtime engine. type WasmEngine interface { // Create will compile the wasm code, and store the resulting pre-compile @@ -51,13 +62,14 @@ type WasmEngine interface { // Under the hood, we may recompile the wasm, use a cached native compile, or even use a cached instance // for performance. Instantiate( + ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, initMsg []byte, - store wasmvm.KVStore, + store PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -70,13 +82,14 @@ type WasmEngine interface { // The caller is responsible for passing the correct `store` (which must have been initialized exactly once), // and setting the env with relevant info on this instance (address, balance, etc) Execute( + ctx sdk.Context, code wasmvm.Checksum, env wasmvmtypes.Env, info wasmvmtypes.MessageInfo, executeMsg []byte, - store wasmvm.KVStore, + store PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -86,12 +99,13 @@ type WasmEngine interface { // valid json-encoded data to return to the client. // The meaning of path and data can be determined by the code. Path is the suffix of the abci.QueryRequest.Path Query( + ctx sdk.Context, code wasmvm.Checksum, env wasmvmtypes.Env, queryMsg []byte, - store wasmvm.KVStore, + store PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -104,12 +118,13 @@ type WasmEngine interface { // // MigrateMsg has some data on how to perform the migration. Migrate( + ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, migrateMsg []byte, - store wasmvm.KVStore, + store PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -121,12 +136,13 @@ type WasmEngine interface { // This allows a contract to expose custom "super user" functions or priviledged operations that can be // deeply integrated with native modules. Sudo( + ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, sudoMsg []byte, - store wasmvm.KVStore, + store PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -134,12 +150,13 @@ type WasmEngine interface { // Reply is called on the original dispatching contract after running a submessage Reply( + ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, reply wasmvmtypes.Reply, - store wasmvm.KVStore, + store PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -160,12 +177,13 @@ type WasmEngine interface { // IBCChannelOpen is available on IBC-enabled contracts and is a hook to call into // during the handshake phase IBCChannelOpen( + ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelOpenMsg, - store wasmvm.KVStore, + store PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -174,12 +192,13 @@ type WasmEngine interface { // IBCChannelConnect is available on IBC-enabled contracts and is a hook to call into // during the handshake phase IBCChannelConnect( + ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelConnectMsg, - store wasmvm.KVStore, + store PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -188,12 +207,13 @@ type WasmEngine interface { // IBCChannelClose is available on IBC-enabled contracts and is a hook to call into // at the end of the channel lifetime IBCChannelClose( + ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, channel wasmvmtypes.IBCChannelCloseMsg, - store wasmvm.KVStore, + store PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -202,12 +222,13 @@ type WasmEngine interface { // IBCPacketReceive is available on IBC-enabled contracts and is called when an incoming // packet is received on a channel belonging to this contract IBCPacketReceive( + ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, packet wasmvmtypes.IBCPacketReceiveMsg, - store wasmvm.KVStore, + store PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -217,12 +238,13 @@ type WasmEngine interface { // the response for an outgoing packet (previously sent by this contract) // is received IBCPacketAck( + ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, ack wasmvmtypes.IBCPacketAckMsg, - store wasmvm.KVStore, + store PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -232,12 +254,13 @@ type WasmEngine interface { // outgoing packet (previously sent by this contract) will probably never be executed. // Usually handled like ack returning an error IBCPacketTimeout( + ctx sdk.Context, checksum wasmvm.Checksum, env wasmvmtypes.Env, packet wasmvmtypes.IBCPacketTimeoutMsg, - store wasmvm.KVStore, + store PrefixStoreInfo, goapi wasmvm.GoAPI, - querier wasmvm.Querier, + querier QuerierWithCtx, gasMeter wasmvm.GasMeter, gasLimit uint64, deserCost wasmvmtypes.UFraction, @@ -256,6 +279,9 @@ type WasmEngine interface { // GetMetrics some internal metrics for monitoring purposes. GetMetrics() (*wasmvmtypes.Metrics, error) + + // SetGasRecorder sets the gas recorder that records contract gas usage + SetGasRecorder(gasRecorder ContractGasProcessor) } var _ wasmvm.KVStore = &StoreAdapter{}