Skip to content

Commit

Permalink
feat: add custom queries to wasm module (#5261)
Browse files Browse the repository at this point in the history
* feat: add custom queries to wasm module

Signed-off-by: aeryz <[email protected]>

* linter stuff as per usual.

* review comments

Signed-off-by: aeryz <[email protected]>

* lint

Signed-off-by: aeryz <[email protected]>

* Use wasmvm.Querier.

* Lint this bad boy

* Small test nits.

* add default querier and update documentation

---------

Signed-off-by: aeryz <[email protected]>
Co-authored-by: DimitrisJim <[email protected]>
Co-authored-by: Carlos Rodriguez <[email protected]>
  • Loading branch information
3 people authored and damiannolan committed Dec 5, 2023
1 parent 30ba757 commit 4575cc3
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 8 deletions.
13 changes: 12 additions & 1 deletion docs/docs/03-light-clients/04-wasm/03-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,18 @@ func NewSimApp(
// This is the recommended approach when the chain
// also uses `x/wasm`, and then the Wasm VM instance
// can be shared.
// This sample code uses also an implementation of the
// wasmvm.Querier interface (querier). If nil is passed
// instead, then a default querier will be used that
// returns an error for all query types.
// See the section below for more information.
app.WasmClientKeeper = wasmkeeper.NewKeeperWithVM(
appCodec,
runtime.NewKVStoreService(keys[wasmtypes.StoreKey]),
app.IBCKeeper.ClientKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
wasmVM,
querier,
)
app.ModuleManager = module.NewManager(
// SDK app modules
Expand Down Expand Up @@ -125,7 +130,7 @@ func NewSimApp(

## Keeper instantiation

When it comes to instantiating `08-wasm`'s keeper there are two recommended ways of doing it. Choosing one or the other will depend on whether the chain already integrates [`x/wasm`](https://github.com/CosmWasm/wasmd/tree/main/x/wasm) or not.
When it comes to instantiating `08-wasm`'s keeper there are two recommended ways of doing it. Choosing one or the other will depend on whether the chain already integrates [`x/wasm`](https://github.com/CosmWasm/wasmd/tree/main/x/wasm) or not. Both available constructor functions accept a querier parameter that should implement the [`Querier` interface of `wasmvm`](https://github.com/CosmWasm/wasmvm/blob/v1.5.0/types/queries.go#L37). If `nil` is provided, then a default querier implementation is used that returns error for any query type.

### If `x/wasm` is present

Expand Down Expand Up @@ -194,12 +199,17 @@ app.WasmKeeper = wasmkeeper.NewKeeper(
wasmOpts...,
)

// This sample code uses also an implementation of the
// wasmvm.Querier interface (querier). If nil is passed
// instead, then a default querier will be used that
// returns an error for all query types.
app.WasmClientKeeper = wasmkeeper.NewKeeperWithVM(
appCodec,
runtime.NewKVStoreService(keys[wasmtypes.StoreKey]),
app.IBCKeeper.ClientKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
wasmer, // pass the Wasm VM instance to `08-wasm` keeper constructor
querier,
)
...
```
Expand Down Expand Up @@ -240,6 +250,7 @@ app.WasmClientKeeper = wasmkeeper.NewKeeperWithConfig(
app.IBCKeeper.ClientKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
wasmConfig,
querier
)
```

Expand Down
1 change: 1 addition & 0 deletions docs/docs/03-light-clients/04-wasm/05-governance.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ app.WasmClientKeeper = wasmkeeper.NewKeeperWithVM(
app.IBCKeeper.ClientKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(), // authority
wasmVM,
querier,
)
```

Expand Down
17 changes: 17 additions & 0 deletions modules/light-clients/08-wasm/internal/ibcwasm/querier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ibcwasm

import (
"errors"

wasmvmtypes "github.com/CosmWasm/wasmvm/types"
)

type defaultQuerier struct{}

func (*defaultQuerier) GasConsumed() uint64 {
return 0
}

func (*defaultQuerier) Query(_ wasmvmtypes.QueryRequest, _ uint64) ([]byte, error) {
return nil, errors.New("queries in contract are not allowed")
}
19 changes: 19 additions & 0 deletions modules/light-clients/08-wasm/internal/ibcwasm/wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ package ibcwasm
import (
"errors"

wasmvm "github.com/CosmWasm/wasmvm"

"cosmossdk.io/collections"
storetypes "cosmossdk.io/core/store"
)

var (
vm WasmEngine

querier wasmvm.Querier

// state management
Schema collections.Schema
Checksums collections.KeySet[[]byte]
Expand All @@ -32,6 +36,21 @@ func GetVM() WasmEngine {
return vm
}

// SetQuerier sets the custom wasm query handle for the 08-wasm module.
// If wasmQuerier is nil a default querier is used that return always an error for any query.
func SetQuerier(wasmQuerier wasmvm.Querier) {
if wasmQuerier == nil {
querier = &defaultQuerier{}
} else {
querier = wasmQuerier
}
}

// GetQuerier returns the custom wasm query handler for the 08-wasm module.
func GetQuerier() wasmvm.Querier {
return querier
}

// SetupWasmStoreService sets up the 08-wasm module's collections.
func SetupWasmStoreService(storeService storetypes.KVStoreService) {
sb := collections.NewSchemaBuilder(storeService)
Expand Down
5 changes: 4 additions & 1 deletion modules/light-clients/08-wasm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func NewKeeperWithVM(
clientKeeper types.ClientKeeper,
authority string,
vm ibcwasm.WasmEngine,
querier wasmvm.Querier,
) Keeper {
if clientKeeper == nil {
panic(errors.New("client keeper must be not nil"))
Expand All @@ -59,6 +60,7 @@ func NewKeeperWithVM(
}

ibcwasm.SetVM(vm)
ibcwasm.SetQuerier(querier)
ibcwasm.SetupWasmStoreService(storeService)

return Keeper{
Expand All @@ -77,13 +79,14 @@ func NewKeeperWithConfig(
clientKeeper types.ClientKeeper,
authority string,
wasmConfig types.WasmConfig,
querier wasmvm.Querier,
) Keeper {
vm, err := wasmvm.NewVM(wasmConfig.DataDir, wasmConfig.SupportedCapabilities, types.ContractMemoryLimit, wasmConfig.ContractDebugMode, types.MemoryCacheSize)
if err != nil {
panic(fmt.Errorf("failed to instantiate new Wasm VM instance: %v", err))
}

return NewKeeperWithVM(cdc, storeService, clientKeeper, authority, vm)
return NewKeeperWithVM(cdc, storeService, clientKeeper, authority, vm, querier)
}

// GetAuthority returns the 08-wasm module's authority.
Expand Down
5 changes: 5 additions & 0 deletions modules/light-clients/08-wasm/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ func (suite *KeeperTestSuite) TestNewKeeper() {
GetSimApp(suite.chainA).IBCKeeper.ClientKeeper,
GetSimApp(suite.chainA).WasmClientKeeper.GetAuthority(),
ibcwasm.GetVM(),
nil,
)
},
true,
Expand All @@ -170,6 +171,7 @@ func (suite *KeeperTestSuite) TestNewKeeper() {
GetSimApp(suite.chainA).IBCKeeper.ClientKeeper,
"", // authority
ibcwasm.GetVM(),
nil,
)
},
false,
Expand All @@ -184,6 +186,7 @@ func (suite *KeeperTestSuite) TestNewKeeper() {
nil, // client keeper,
GetSimApp(suite.chainA).WasmClientKeeper.GetAuthority(),
ibcwasm.GetVM(),
nil,
)
},
false,
Expand All @@ -198,6 +201,7 @@ func (suite *KeeperTestSuite) TestNewKeeper() {
GetSimApp(suite.chainA).IBCKeeper.ClientKeeper,
GetSimApp(suite.chainA).WasmClientKeeper.GetAuthority(),
nil,
nil,
)
},
false,
Expand All @@ -212,6 +216,7 @@ func (suite *KeeperTestSuite) TestNewKeeper() {
GetSimApp(suite.chainA).IBCKeeper.ClientKeeper,
GetSimApp(suite.chainA).WasmClientKeeper.GetAuthority(),
ibcwasm.GetVM(),
nil,
)
},
false,
Expand Down
4 changes: 2 additions & 2 deletions modules/light-clients/08-wasm/testing/simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,12 +471,12 @@ func NewSimApp(
// NOTE: mockVM is used for testing purposes only!
app.WasmClientKeeper = wasmkeeper.NewKeeperWithVM(
appCodec, runtime.NewKVStoreService(keys[wasmtypes.StoreKey]), app.IBCKeeper.ClientKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(), mockVM,
authtypes.NewModuleAddress(govtypes.ModuleName).String(), mockVM, nil,
)
} else {
app.WasmClientKeeper = wasmkeeper.NewKeeperWithConfig(
appCodec, runtime.NewKVStoreService(keys[wasmtypes.StoreKey]), app.IBCKeeper.ClientKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(), wasmConfig,
authtypes.NewModuleAddress(govtypes.ModuleName).String(), wasmConfig, nil,
)
}

Expand Down
109 changes: 109 additions & 0 deletions modules/light-clients/08-wasm/types/querier_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package types_test

import (
"encoding/json"
"math"

wasmvm "github.com/CosmWasm/wasmvm"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"

"github.com/cosmos/ibc-go/modules/light-clients/08-wasm/internal/ibcwasm"
wasmtesting "github.com/cosmos/ibc-go/modules/light-clients/08-wasm/testing"
"github.com/cosmos/ibc-go/modules/light-clients/08-wasm/types"
"github.com/cosmos/ibc-go/v8/modules/core/exported"
)

type CustomQuery struct {
Echo *QueryEcho `json:"echo,omitempty"`
}

type QueryEcho struct {
Data string `json:"data"`
}

type CustomQueryHandler struct{}

func (*CustomQueryHandler) GasConsumed() uint64 {
return 0
}

func (*CustomQueryHandler) Query(request wasmvmtypes.QueryRequest, gasLimit uint64) ([]byte, error) {
var customQuery CustomQuery
err := json.Unmarshal([]byte(request.Custom), &customQuery)
if err != nil {
return nil, wasmtesting.ErrMockContract
}

if customQuery.Echo != nil {
data, err := json.Marshal(customQuery.Echo.Data)
return data, err
}

return nil, wasmtesting.ErrMockContract
}

func (suite *TypesTestSuite) TestCustomQuery() {
testCases := []struct {
name string
malleate func()
}{
{
"success: custom query",
func() {
ibcwasm.SetQuerier(&CustomQueryHandler{})

suite.mockVM.RegisterQueryCallback(types.StatusMsg{}, 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) {
echo := CustomQuery{
Echo: &QueryEcho{
Data: "hello world",
},
}
echoJSON, err := json.Marshal(echo)
suite.Require().NoError(err)

resp, err := querier.Query(wasmvmtypes.QueryRequest{
Custom: json.RawMessage(echoJSON),
}, math.MaxUint64)
suite.Require().NoError(err)

var respData string
err = json.Unmarshal(resp, &respData)
suite.Require().NoError(err)
suite.Require().Equal("hello world", respData)

resp, err = json.Marshal(types.StatusResult{Status: exported.Active.String()})
suite.Require().NoError(err)

return resp, wasmtesting.DefaultGasUsed, nil
})
},
},
{
"default query",
func() {
suite.mockVM.RegisterQueryCallback(types.StatusMsg{}, 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) {
_, err := querier.Query(wasmvmtypes.QueryRequest{}, math.MaxUint64)
suite.Require().Error(err)

return nil, wasmtesting.DefaultGasUsed, err
})
},
},
}

for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupWasmWithMockVM()

endpoint := wasmtesting.NewWasmEndpoint(suite.chainA)
err := endpoint.CreateClient()
suite.Require().NoError(err)

tc.malleate()

clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), endpoint.ClientID)
clientState := endpoint.GetClientState()
clientState.Status(suite.chainA.GetContext(), clientStore, suite.chainA.App.AppCodec())
})
}
}
8 changes: 4 additions & 4 deletions modules/light-clients/08-wasm/types/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func instantiateContract(ctx sdk.Context, clientStore storetypes.KVStore, checks
}

ctx.GasMeter().ConsumeGas(VMGasRegister.NewContractInstanceCosts(true, len(msg)), "Loading CosmWasm module: instantiate")
response, gasUsed, err := ibcwasm.GetVM().Instantiate(checksum, env, msgInfo, msg, newStoreAdapter(clientStore), wasmvmAPI, nil, multipliedGasMeter, gasLimit, costJSONDeserialization)
response, gasUsed, err := ibcwasm.GetVM().Instantiate(checksum, env, msgInfo, msg, newStoreAdapter(clientStore), wasmvmAPI, ibcwasm.GetQuerier(), multipliedGasMeter, gasLimit, costJSONDeserialization)
VMGasRegister.consumeRuntimeGas(ctx, gasUsed)
return response, err
}
Expand All @@ -67,7 +67,7 @@ func callContract(ctx sdk.Context, clientStore storetypes.KVStore, checksum Chec
env := getEnv(ctx, clientID)

ctx.GasMeter().ConsumeGas(VMGasRegister.InstantiateContractCosts(true, len(msg)), "Loading CosmWasm module: sudo")
resp, gasUsed, err := ibcwasm.GetVM().Sudo(checksum, env, msg, newStoreAdapter(clientStore), wasmvmAPI, nil, multipliedGasMeter, gasLimit, costJSONDeserialization)
resp, gasUsed, err := ibcwasm.GetVM().Sudo(checksum, env, msg, newStoreAdapter(clientStore), wasmvmAPI, ibcwasm.GetQuerier(), multipliedGasMeter, gasLimit, costJSONDeserialization)
VMGasRegister.consumeRuntimeGas(ctx, gasUsed)
return resp, err
}
Expand All @@ -81,7 +81,7 @@ func migrateContract(ctx sdk.Context, clientID string, clientStore storetypes.KV
env := getEnv(ctx, clientID)

ctx.GasMeter().ConsumeGas(VMGasRegister.InstantiateContractCosts(true, len(msg)), "Loading CosmWasm module: migrate")
resp, gasUsed, err := ibcwasm.GetVM().Migrate(checksum, env, msg, newStoreAdapter(clientStore), wasmvmAPI, nil, multipliedGasMeter, gasLimit, costJSONDeserialization)
resp, gasUsed, err := ibcwasm.GetVM().Migrate(checksum, env, msg, newStoreAdapter(clientStore), wasmvmAPI, ibcwasm.GetQuerier(), multipliedGasMeter, gasLimit, costJSONDeserialization)
VMGasRegister.consumeRuntimeGas(ctx, gasUsed)
return resp, err
}
Expand All @@ -99,7 +99,7 @@ func queryContract(ctx sdk.Context, clientStore storetypes.KVStore, checksum Che
env := getEnv(ctx, clientID)

ctx.GasMeter().ConsumeGas(VMGasRegister.InstantiateContractCosts(true, len(msg)), "Loading CosmWasm module: query")
resp, gasUsed, err := ibcwasm.GetVM().Query(checksum, env, msg, newStoreAdapter(clientStore), wasmvmAPI, nil, multipliedGasMeter, gasLimit, costJSONDeserialization)
resp, gasUsed, err := ibcwasm.GetVM().Query(checksum, env, msg, newStoreAdapter(clientStore), wasmvmAPI, ibcwasm.GetQuerier(), multipliedGasMeter, gasLimit, costJSONDeserialization)
VMGasRegister.consumeRuntimeGas(ctx, gasUsed)
return resp, err
}
Expand Down

0 comments on commit 4575cc3

Please sign in to comment.