Some checks failed
CodeQL / Analyze (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Deploy to GitHub Pages / Deploy to GitHub Pages (push) Has been cancelled
Buf-Push / push (push) Has been cancelled
182 lines
5.6 KiB
Go
182 lines
5.6 KiB
Go
package keeper
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"slices"
|
|
|
|
wasmvmtypes "github.com/CosmWasm/wasmvm/v2/types"
|
|
|
|
errorsmod "cosmossdk.io/errors"
|
|
storetypes "cosmossdk.io/store/types"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
|
|
abci "github.com/cometbft/cometbft/abci/types"
|
|
|
|
"github.com/cosmos/ibc-go/modules/light-clients/08-wasm/v10/types"
|
|
)
|
|
|
|
/*
|
|
`queryHandler` is a contextual querier which references the global `ibcwasm.QueryPluginsI`
|
|
to handle queries. The global `ibcwasm.QueryPluginsI` points to a `types.QueryPlugins` which
|
|
contains two sub-queriers: `types.CustomQuerier` and `types.StargateQuerier`. These sub-queriers
|
|
can be replaced by the user through the options api in the keeper.
|
|
|
|
In addition, the `types.StargateQuerier` references a global `types.QueryRouter` which points
|
|
to `baseapp.GRPCQueryRouter`.
|
|
|
|
This design is based on wasmd's (v0.50.0) querier plugin design.
|
|
*/
|
|
|
|
var _ wasmvmtypes.Querier = (*queryHandler)(nil)
|
|
|
|
// defaultAcceptList defines a set of default allowed queries made available to the Querier.
|
|
var defaultAcceptList = []string{
|
|
"/ibc.core.client.v1.Query/VerifyMembership",
|
|
}
|
|
|
|
// queryHandler is a wrapper around the sdk.Context and the CallerID that calls
|
|
// into the query plugins.
|
|
type queryHandler struct {
|
|
Ctx sdk.Context
|
|
Plugins QueryPlugins
|
|
CallerID string
|
|
}
|
|
|
|
// newQueryHandler returns a default querier that can be used in the contract.
|
|
func newQueryHandler(ctx sdk.Context, plugins QueryPlugins, callerID string) *queryHandler {
|
|
return &queryHandler{
|
|
Ctx: ctx,
|
|
Plugins: plugins,
|
|
CallerID: callerID,
|
|
}
|
|
}
|
|
|
|
// GasConsumed implements the wasmvmtypes.Querier interface.
|
|
func (q *queryHandler) GasConsumed() uint64 {
|
|
return VMGasRegister.ToWasmVMGas(q.Ctx.GasMeter().GasConsumed())
|
|
}
|
|
|
|
// Query implements the wasmvmtypes.Querier interface.
|
|
func (q *queryHandler) Query(request wasmvmtypes.QueryRequest, gasLimit uint64) ([]byte, error) {
|
|
sdkGas := VMGasRegister.FromWasmVMGas(gasLimit)
|
|
|
|
// discard all changes/events in subCtx by not committing the cached context
|
|
subCtx, _ := q.Ctx.WithGasMeter(storetypes.NewGasMeter(sdkGas)).CacheContext()
|
|
|
|
// make sure we charge the higher level context even on panic
|
|
defer func() {
|
|
q.Ctx.GasMeter().ConsumeGas(subCtx.GasMeter().GasConsumed(), "contract sub-query")
|
|
}()
|
|
|
|
res, err := q.Plugins.HandleQuery(subCtx, q.CallerID, request)
|
|
if err == nil {
|
|
return res, nil
|
|
}
|
|
|
|
moduleLogger(q.Ctx).Debug("Redacting query error", "cause", err)
|
|
return nil, redactError(err)
|
|
}
|
|
|
|
type (
|
|
CustomQuerier func(ctx sdk.Context, request json.RawMessage) ([]byte, error)
|
|
StargateQuerier func(ctx sdk.Context, request *wasmvmtypes.StargateQuery) ([]byte, error)
|
|
|
|
// QueryPlugins is a list of queriers that can be used to extend the default querier.
|
|
QueryPlugins struct {
|
|
Custom CustomQuerier
|
|
Stargate StargateQuerier
|
|
}
|
|
)
|
|
|
|
// Merge merges the query plugin with a provided one.
|
|
func (e QueryPlugins) Merge(x *QueryPlugins) QueryPlugins {
|
|
// only update if this is non-nil and then only set values
|
|
if x == nil {
|
|
return e
|
|
}
|
|
|
|
if x.Custom != nil {
|
|
e.Custom = x.Custom
|
|
}
|
|
|
|
if x.Stargate != nil {
|
|
e.Stargate = x.Stargate
|
|
}
|
|
|
|
return e
|
|
}
|
|
|
|
// HandleQuery implements the ibcwasm.QueryPluginsI interface.
|
|
func (e QueryPlugins) HandleQuery(ctx sdk.Context, caller string, request wasmvmtypes.QueryRequest) ([]byte, error) {
|
|
if request.Stargate != nil {
|
|
return e.Stargate(ctx, request.Stargate)
|
|
}
|
|
|
|
if request.Custom != nil {
|
|
return e.Custom(ctx, request.Custom)
|
|
}
|
|
|
|
return nil, wasmvmtypes.UnsupportedRequest{Kind: "Unsupported query request"}
|
|
}
|
|
|
|
// NewDefaultQueryPlugins returns the default set of query plugins
|
|
func NewDefaultQueryPlugins(queryRouter types.QueryRouter) QueryPlugins {
|
|
return QueryPlugins{
|
|
Custom: RejectCustomQuerier(),
|
|
Stargate: AcceptListStargateQuerier([]string{}, queryRouter),
|
|
}
|
|
}
|
|
|
|
// AcceptListStargateQuerier allows all queries that are in the provided accept list.
|
|
// This function returns protobuf encoded responses in bytes.
|
|
func AcceptListStargateQuerier(acceptedQueries []string, queryRouter types.QueryRouter) func(sdk.Context, *wasmvmtypes.StargateQuery) ([]byte, error) {
|
|
return func(ctx sdk.Context, request *wasmvmtypes.StargateQuery) ([]byte, error) {
|
|
// append user defined accepted queries to default list defined above.
|
|
acceptedQueries = append(defaultAcceptList, acceptedQueries...)
|
|
|
|
isAccepted := slices.Contains(acceptedQueries, request.Path)
|
|
if !isAccepted {
|
|
return nil, wasmvmtypes.UnsupportedRequest{Kind: fmt.Sprintf("'%s' path is not allowed from the contract", request.Path)}
|
|
}
|
|
|
|
route := queryRouter.Route(request.Path)
|
|
if route == nil {
|
|
return nil, wasmvmtypes.UnsupportedRequest{Kind: fmt.Sprintf("No route to query '%s'", request.Path)}
|
|
}
|
|
|
|
res, err := route(ctx, &abci.RequestQuery{
|
|
Data: request.Data,
|
|
Path: request.Path,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if res == nil || res.Value == nil {
|
|
return nil, wasmvmtypes.InvalidResponse{Err: "Query response is empty"}
|
|
}
|
|
|
|
return res.Value, nil
|
|
}
|
|
}
|
|
|
|
// RejectCustomQuerier rejects all custom queries
|
|
func RejectCustomQuerier() func(sdk.Context, json.RawMessage) ([]byte, error) {
|
|
return func(ctx sdk.Context, request json.RawMessage) ([]byte, error) {
|
|
return nil, wasmvmtypes.UnsupportedRequest{Kind: "Custom queries are not allowed"}
|
|
}
|
|
}
|
|
|
|
// Wasmd Issue [#759](https://github.com/CosmWasm/wasmd/issues/759)
|
|
// Don't return error string for worries of non-determinism
|
|
func redactError(err error) error {
|
|
// Do not redact system errors
|
|
// SystemErrors must be created in 08-wasm and we can ensure determinism
|
|
if wasmvmtypes.ToSystemError(err) != nil {
|
|
return err
|
|
}
|
|
|
|
codespace, code, _ := errorsmod.ABCIInfo(err, false)
|
|
return fmt.Errorf("codespace: %s, code: %d", codespace, code)
|
|
}
|