mukan-ibc/modules/light-clients/08-wasm/keeper/querier_test.go
Mukan Erkin Törük 6852832fe8
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
initial: sovereign Mukan Network fork
2026-05-11 03:18:28 +03:00

352 lines
12 KiB
Go

package keeper_test
import (
"encoding/hex"
"encoding/json"
"fmt"
"math"
wasmvm "github.com/CosmWasm/wasmvm/v2"
wasmvmtypes "github.com/CosmWasm/wasmvm/v2/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/ibc-go/modules/light-clients/08-wasm/v10/keeper"
wasmtesting "github.com/cosmos/ibc-go/modules/light-clients/08-wasm/v10/testing"
"github.com/cosmos/ibc-go/modules/light-clients/08-wasm/v10/types"
clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types"
commitmenttypes "github.com/cosmos/ibc-go/v10/modules/core/23-commitment/types"
"github.com/cosmos/ibc-go/v10/modules/core/exported"
)
type CustomQuery struct {
Echo *QueryEcho `json:"echo,omitempty"`
}
type QueryEcho struct {
Data string `json:"data"`
}
func mockCustomQuerier() func(sdk.Context, json.RawMessage) ([]byte, error) {
return func(ctx sdk.Context, request json.RawMessage) ([]byte, error) {
var customQuery CustomQuery
err := json.Unmarshal([]byte(request), &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 *KeeperTestSuite) TestCustomQuery() {
testCases := []struct {
name string
malleate func()
expError error
}{
{
"success: custom query",
func() {
querierPlugin := keeper.QueryPlugins{
Custom: mockCustomQuerier(),
}
GetSimApp(suite.chainA).WasmClientKeeper.SetQueryPlugins(querierPlugin)
suite.mockVM.RegisterQueryCallback(types.StatusMsg{}, func(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ []byte, _ wasmvm.KVStore, _ wasmvm.GoAPI, querier wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.QueryResult, 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 &wasmvmtypes.QueryResult{Ok: resp}, wasmtesting.DefaultGasUsed, nil
})
},
nil,
},
{
"failure: default query",
func() {
suite.mockVM.RegisterQueryCallback(types.StatusMsg{}, func(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ []byte, _ wasmvm.KVStore, _ wasmvm.GoAPI, querier wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.QueryResult, uint64, error) {
resp, err := querier.Query(wasmvmtypes.QueryRequest{Custom: json.RawMessage("{}")}, math.MaxUint64)
suite.Require().ErrorIs(err, wasmvmtypes.UnsupportedRequest{Kind: "Custom queries are not allowed"})
suite.Require().Nil(resp)
return nil, wasmtesting.DefaultGasUsed, err
})
},
types.ErrVMError,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupWasmWithMockVM()
_ = suite.storeWasmCode(wasmtesting.Code)
endpoint := wasmtesting.NewWasmEndpoint(suite.chainA)
err := endpoint.CreateClient()
suite.Require().NoError(err)
tc.malleate()
wasmClientKeeper := GetSimApp(suite.chainA).WasmClientKeeper
clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), endpoint.ClientID)
clientState, ok := endpoint.GetClientState().(*types.ClientState)
suite.Require().True(ok)
res, err := wasmClientKeeper.WasmQuery(suite.chainA.GetContext(), endpoint.ClientID, clientStore, clientState, types.QueryMsg{Status: &types.StatusMsg{}})
if tc.expError == nil {
suite.Require().Nil(err)
suite.Require().NotNil(res)
} else {
suite.Require().Nil(res)
suite.Require().ErrorIs(err, tc.expError)
}
// reset query plugins after each test
wasmClientKeeper.SetQueryPlugins(keeper.NewDefaultQueryPlugins(GetSimApp(suite.chainA).GRPCQueryRouter()))
})
}
}
func (suite *KeeperTestSuite) TestStargateQuery() {
typeURL := "/ibc.lightclients.wasm.v1.Query/Checksums"
var (
endpoint *wasmtesting.WasmEndpoint
checksum []byte
expDiscardedState = false
proofKey = []byte("mock-key")
testKey = []byte("test-key")
value = []byte("mock-value")
)
testCases := []struct {
name string
malleate func()
expError error
}{
{
"success: custom query",
func() {
querierPlugin := keeper.QueryPlugins{
Stargate: keeper.AcceptListStargateQuerier([]string{typeURL}, GetSimApp(suite.chainA).GRPCQueryRouter()),
}
GetSimApp(suite.chainA).WasmClientKeeper.SetQueryPlugins(querierPlugin)
suite.mockVM.RegisterQueryCallback(types.TimestampAtHeightMsg{}, func(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ []byte, store wasmvm.KVStore, _ wasmvm.GoAPI, querier wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.QueryResult, uint64, error) {
queryRequest := types.QueryChecksumsRequest{}
bz, err := queryRequest.Marshal()
suite.Require().NoError(err)
resp, err := querier.Query(wasmvmtypes.QueryRequest{
Stargate: &wasmvmtypes.StargateQuery{
Path: typeURL,
Data: bz,
},
}, math.MaxUint64)
suite.Require().NoError(err)
var respData types.QueryChecksumsResponse
err = respData.Unmarshal(resp)
suite.Require().NoError(err)
expChecksum := hex.EncodeToString(checksum)
suite.Require().Len(respData.Checksums, 1)
suite.Require().Equal(expChecksum, respData.Checksums[0])
store.Set(testKey, value)
result, err := json.Marshal(types.TimestampAtHeightResult{})
suite.Require().NoError(err)
return &wasmvmtypes.QueryResult{Ok: result}, wasmtesting.DefaultGasUsed, nil
})
},
nil,
},
{
// The following test sets a mock proof key and value in the ibc store and registers a query callback on the Status msg.
// The Status handler will then perform the QueryVerifyMembershipRequest using the wasmvm.Querier.
// As the VerifyMembership query rpc will call directly back into the same client, we also register a callback for VerifyMembership.
// Here we decode the proof and verify the mock proof key and value are set in the ibc store.
// This exercises the full flow through the grpc handler and into the light client for verification, handling encoding and routing.
// Furthermore we write a test key and assert that the state changes made by this handler were discarded by the cachedCtx at the grpc handler.
"success: verify membership query",
func() {
querierPlugin := keeper.QueryPlugins{
Stargate: keeper.AcceptListStargateQuerier([]string{""}, GetSimApp(suite.chainA).GRPCQueryRouter()),
}
GetSimApp(suite.chainA).WasmClientKeeper.SetQueryPlugins(querierPlugin)
store := suite.chainA.GetContext().KVStore(GetSimApp(suite.chainA).GetKey(exported.StoreKey))
store.Set(proofKey, value)
suite.coordinator.CommitBlock(suite.chainA)
proof, proofHeight := endpoint.QueryProofAtHeight(proofKey, uint64(suite.chainA.GetContext().BlockHeight()))
merklePath := commitmenttypes.NewMerklePath(proofKey)
merklePath, err := commitmenttypes.ApplyPrefix(suite.chainA.GetPrefix(), merklePath)
suite.Require().NoError(err)
suite.mockVM.RegisterQueryCallback(types.TimestampAtHeightMsg{}, func(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ []byte, _ wasmvm.KVStore, _ wasmvm.GoAPI, querier wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.QueryResult, uint64, error) {
queryRequest := clienttypes.QueryVerifyMembershipRequest{
ClientId: endpoint.ClientID,
Proof: proof,
ProofHeight: proofHeight,
MerklePath: merklePath,
Value: value,
}
bz, err := queryRequest.Marshal()
suite.Require().NoError(err)
resp, err := querier.Query(wasmvmtypes.QueryRequest{
Stargate: &wasmvmtypes.StargateQuery{
Path: "/ibc.core.client.v1.Query/VerifyMembership",
Data: bz,
},
}, math.MaxUint64)
suite.Require().NoError(err)
var respData clienttypes.QueryVerifyMembershipResponse
err = respData.Unmarshal(resp)
suite.Require().NoError(err)
suite.Require().True(respData.Success)
result, err := json.Marshal(types.TimestampAtHeightResult{})
suite.Require().NoError(err)
return &wasmvmtypes.QueryResult{Ok: result}, wasmtesting.DefaultGasUsed, nil
})
suite.mockVM.RegisterSudoCallback(types.VerifyMembershipMsg{}, func(_ wasmvm.Checksum, _ wasmvmtypes.Env, sudoMsg []byte, store wasmvm.KVStore,
_ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction,
) (*wasmvmtypes.ContractResult, uint64, error) {
var payload types.SudoMsg
err := json.Unmarshal(sudoMsg, &payload)
suite.Require().NoError(err)
var merkleProof commitmenttypes.MerkleProof
err = suite.chainA.Codec.Unmarshal(payload.VerifyMembership.Proof, &merkleProof)
suite.Require().NoError(err)
root := commitmenttypes.NewMerkleRoot(suite.chainA.App.LastCommitID().Hash)
err = merkleProof.VerifyMembership(commitmenttypes.GetSDKSpecs(), root, merklePath, payload.VerifyMembership.Value)
suite.Require().NoError(err)
bz, err := json.Marshal(types.EmptyResult{})
suite.Require().NoError(err)
expDiscardedState = true
store.Set(testKey, value)
return &wasmvmtypes.ContractResult{Ok: &wasmvmtypes.Response{Data: bz}}, wasmtesting.DefaultGasUsed, nil
})
},
nil,
},
{
"failure: default querier",
func() {
suite.mockVM.RegisterQueryCallback(types.TimestampAtHeightMsg{}, func(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ []byte, store wasmvm.KVStore, _ wasmvm.GoAPI, querier wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.QueryResult, uint64, error) {
queryRequest := types.QueryChecksumsRequest{}
bz, err := queryRequest.Marshal()
suite.Require().NoError(err)
resp, err := querier.Query(wasmvmtypes.QueryRequest{
Stargate: &wasmvmtypes.StargateQuery{
Path: typeURL,
Data: bz,
},
}, math.MaxUint64)
suite.Require().ErrorIs(err, wasmvmtypes.UnsupportedRequest{Kind: fmt.Sprintf("'%s' path is not allowed from the contract", typeURL)})
suite.Require().Nil(resp)
store.Set(testKey, value)
return nil, wasmtesting.DefaultGasUsed, err
})
},
wasmvmtypes.UnsupportedRequest{Kind: fmt.Sprintf("'%s' path is not allowed from the contract", typeURL)},
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
expDiscardedState = false
suite.SetupWasmWithMockVM()
checksum = suite.storeWasmCode(wasmtesting.Code)
endpoint = wasmtesting.NewWasmEndpoint(suite.chainA)
err := endpoint.CreateClient()
suite.Require().NoError(err)
tc.malleate()
wasmClientKeeper := GetSimApp(suite.chainA).WasmClientKeeper
payload := types.QueryMsg{
TimestampAtHeight: &types.TimestampAtHeightMsg{
Height: clienttypes.NewHeight(1, 100),
},
}
clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), endpoint.ClientID)
clientState, ok := endpoint.GetClientState().(*types.ClientState)
suite.Require().True(ok)
// NOTE: we register query callbacks against: types.TimestampAtHeightMsg{}
// in practise, this can against any client state msg, however registering against types.StatusMsg{} introduces recursive loops
// due to test case: "success: verify membership query"
res, err := wasmClientKeeper.WasmQuery(suite.chainA.GetContext(), endpoint.ClientID, clientStore, clientState, payload)
if tc.expError == nil {
suite.Require().NoError(err)
suite.Require().NotNil(res)
} else {
suite.Require().Nil(res)
// use error contains as wasmvm errors do not implement errors.Is method
suite.Require().ErrorContains(err, tc.expError.Error())
}
clientStore = suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), endpoint.ClientID)
if expDiscardedState {
suite.Require().False(clientStore.Has(testKey))
} else {
suite.Require().True(clientStore.Has(testKey))
}
// reset query plugins after each test
wasmClientKeeper.SetQueryPlugins(keeper.NewDefaultQueryPlugins(GetSimApp(suite.chainA).GRPCQueryRouter()))
})
}
}