mukan-ibc/modules/light-clients/08-wasm/keeper/keeper_test.go
Mukan Erkin Törük 88dd97a9f8
Some checks failed
CodeQL / Analyze (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
Docker Build & Push Simapp (main) / docker-build (push) Has been cancelled
refactor: replace all github.com upstream refs with git.cw.tr/mukan-network
2026-05-11 03:36:22 +03:00

549 lines
17 KiB
Go

package keeper_test
import (
"encoding/json"
"errors"
"testing"
wasmvm "github.com/CosmWasm/wasmvm/v2"
wasmvmtypes "github.com/CosmWasm/wasmvm/v2/types"
dbm "github.com/cosmos/cosmos-db"
testifysuite "github.com/stretchr/testify/suite"
"cosmossdk.io/log"
storetypes "cosmossdk.io/store/types"
"git.cw.tr/mukan-network/mukan-sdk/baseapp"
"git.cw.tr/mukan-network/mukan-sdk/runtime"
simtestutil "git.cw.tr/mukan-network/mukan-sdk/testutil/sims"
authtypes "git.cw.tr/mukan-network/mukan-sdk/x/auth/types"
govtypes "git.cw.tr/mukan-network/mukan-sdk/x/gov/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/testing/simapp"
"github.com/cosmos/ibc-go/modules/light-clients/08-wasm/v10/types"
clienttypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/02-client/types"
host "git.cw.tr/mukan-network/mukan-ibc/modules/core/24-host"
"git.cw.tr/mukan-network/mukan-ibc/modules/core/exported"
ibctm "git.cw.tr/mukan-network/mukan-ibc/modules/light-clients/07-tendermint"
ibctesting "git.cw.tr/mukan-network/mukan-ibc/testing"
)
const (
defaultWasmClientID = "08-wasm-0"
)
type KeeperTestSuite struct {
testifysuite.Suite
coordinator *ibctesting.Coordinator
// mockVM is a mock wasm VM that implements the WasmEngine interface
mockVM *wasmtesting.MockWasmEngine
chainA *ibctesting.TestChain
}
// setupTestingApp provides the duplicated simapp which is specific to the 08-wasm module on chain creation.
func setupTestingApp() (ibctesting.TestingApp, map[string]json.RawMessage) {
db := dbm.NewMemDB()
app := simapp.NewUnitTestSimApp(log.NewNopLogger(), db, nil, true, simtestutil.EmptyAppOptions{}, nil)
return app, app.DefaultGenesis()
}
// GetSimApp returns the duplicated SimApp from within the 08-wasm directory.
// This must be used instead of chain.GetSimApp() for tests within this directory.
func GetSimApp(chain *ibctesting.TestChain) *simapp.SimApp {
app, ok := chain.App.(*simapp.SimApp)
if !ok {
panic(errors.New("chain is not a simapp.SimApp"))
}
return app
}
func (suite *KeeperTestSuite) SetupTest() {
suite.coordinator = ibctesting.NewCustomAppCoordinator(suite.T(), 1, setupTestingApp)
suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1))
queryHelper := baseapp.NewQueryServerTestHelper(suite.chainA.GetContext(), GetSimApp(suite.chainA).InterfaceRegistry())
types.RegisterQueryServer(queryHelper, GetSimApp(suite.chainA).WasmClientKeeper)
}
// SetupWasmWithMockVM sets up mock cometbft chain with a mock vm.
func (suite *KeeperTestSuite) SetupWasmWithMockVM() {
suite.coordinator = ibctesting.NewCustomAppCoordinator(suite.T(), 1, suite.setupWasmWithMockVM)
suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1))
}
func (suite *KeeperTestSuite) setupWasmWithMockVM() (ibctesting.TestingApp, map[string]json.RawMessage) {
suite.mockVM = wasmtesting.NewMockWasmEngine()
suite.mockVM.InstantiateFn = func(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.ContractResult, uint64, error) {
var payload types.InstantiateMessage
err := json.Unmarshal(initMsg, &payload)
suite.Require().NoError(err)
wrappedClientState, ok := clienttypes.MustUnmarshalClientState(suite.chainA.App.AppCodec(), payload.ClientState).(*ibctm.ClientState)
suite.Require().True(ok)
clientState := types.NewClientState(payload.ClientState, payload.Checksum, wrappedClientState.LatestHeight)
clientStateBz := clienttypes.MustMarshalClientState(suite.chainA.App.AppCodec(), clientState)
store.Set(host.ClientStateKey(), clientStateBz)
consensusState := types.NewConsensusState(payload.ConsensusState)
consensusStateBz := clienttypes.MustMarshalConsensusState(suite.chainA.App.AppCodec(), consensusState)
store.Set(host.ConsensusStateKey(clientState.LatestHeight), consensusStateBz)
resp, err := json.Marshal(types.EmptyResult{})
suite.Require().NoError(err)
return &wasmvmtypes.ContractResult{Ok: &wasmvmtypes.Response{Data: resp}}, 0, nil
}
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) (*wasmvmtypes.QueryResult, uint64, error) {
resp, err := json.Marshal(types.StatusResult{Status: exported.Active.String()})
suite.Require().NoError(err)
return &wasmvmtypes.QueryResult{Ok: resp}, wasmtesting.DefaultGasUsed, nil
})
db := dbm.NewMemDB()
app := simapp.NewUnitTestSimApp(log.NewNopLogger(), db, nil, true, simtestutil.EmptyAppOptions{}, suite.mockVM)
return app, app.DefaultGenesis()
}
// storeWasmCode stores the wasm code on chain and returns the checksum.
func (suite *KeeperTestSuite) storeWasmCode(wasmCode []byte) []byte {
ctx := suite.chainA.GetContext().WithBlockGasMeter(storetypes.NewInfiniteGasMeter())
msg := types.NewMsgStoreCode(authtypes.NewModuleAddress(govtypes.ModuleName).String(), wasmCode)
response, err := GetSimApp(suite.chainA).WasmClientKeeper.StoreCode(ctx, msg)
suite.Require().NoError(err)
suite.Require().NotNil(response.Checksum)
return response.Checksum
}
func (suite *KeeperTestSuite) SetupSnapshotterWithMockVM() *simapp.SimApp {
suite.mockVM = wasmtesting.NewMockWasmEngine()
return simapp.SetupWithSnapshotter(suite.T(), suite.mockVM)
}
func TestKeeperTestSuite(t *testing.T) {
testifysuite.Run(t, new(KeeperTestSuite))
}
func (suite *KeeperTestSuite) TestNewKeeper() {
testCases := []struct {
name string
instantiateFn func()
expError error
}{
{
"success",
func() {
keeper.NewKeeperWithVM(
GetSimApp(suite.chainA).AppCodec(),
runtime.NewKVStoreService(GetSimApp(suite.chainA).GetKey(types.StoreKey)),
GetSimApp(suite.chainA).IBCKeeper.ClientKeeper,
GetSimApp(suite.chainA).WasmClientKeeper.GetAuthority(),
GetSimApp(suite.chainA).WasmClientKeeper.GetVM(),
GetSimApp(suite.chainA).GRPCQueryRouter(),
)
},
nil,
},
{
"failure: empty authority",
func() {
keeper.NewKeeperWithVM(
GetSimApp(suite.chainA).AppCodec(),
runtime.NewKVStoreService(GetSimApp(suite.chainA).GetKey(types.StoreKey)),
GetSimApp(suite.chainA).IBCKeeper.ClientKeeper,
"", // authority
GetSimApp(suite.chainA).WasmClientKeeper.GetVM(),
GetSimApp(suite.chainA).GRPCQueryRouter(),
)
},
errors.New("authority must be non-empty"),
},
{
"failure: nil client keeper",
func() {
keeper.NewKeeperWithVM(
GetSimApp(suite.chainA).AppCodec(),
runtime.NewKVStoreService(GetSimApp(suite.chainA).GetKey(types.StoreKey)),
nil, // client keeper,
GetSimApp(suite.chainA).WasmClientKeeper.GetAuthority(),
GetSimApp(suite.chainA).WasmClientKeeper.GetVM(),
GetSimApp(suite.chainA).GRPCQueryRouter(),
)
},
errors.New("client keeper must not be nil"),
},
{
"failure: nil wasm VM",
func() {
keeper.NewKeeperWithVM(
GetSimApp(suite.chainA).AppCodec(),
runtime.NewKVStoreService(GetSimApp(suite.chainA).GetKey(types.StoreKey)),
GetSimApp(suite.chainA).IBCKeeper.ClientKeeper,
GetSimApp(suite.chainA).WasmClientKeeper.GetAuthority(),
nil,
GetSimApp(suite.chainA).GRPCQueryRouter(),
)
},
errors.New("wasm VM must not be nil"),
},
{
"failure: nil store service",
func() {
keeper.NewKeeperWithVM(
GetSimApp(suite.chainA).AppCodec(),
nil,
GetSimApp(suite.chainA).IBCKeeper.ClientKeeper,
GetSimApp(suite.chainA).WasmClientKeeper.GetAuthority(),
GetSimApp(suite.chainA).WasmClientKeeper.GetVM(),
GetSimApp(suite.chainA).GRPCQueryRouter(),
)
},
errors.New("store service must not be nil"),
},
{
"failure: nil query router",
func() {
keeper.NewKeeperWithVM(
GetSimApp(suite.chainA).AppCodec(),
runtime.NewKVStoreService(GetSimApp(suite.chainA).GetKey(types.StoreKey)),
GetSimApp(suite.chainA).IBCKeeper.ClientKeeper,
GetSimApp(suite.chainA).WasmClientKeeper.GetAuthority(),
GetSimApp(suite.chainA).WasmClientKeeper.GetVM(),
nil,
)
},
errors.New("query router must not be nil"),
},
}
for _, tc := range testCases {
tc := tc
suite.SetupTest()
suite.Run(tc.name, func() {
if tc.expError == nil {
suite.Require().NotPanics(
tc.instantiateFn,
)
} else {
suite.Require().PanicsWithError(tc.expError.Error(), func() {
tc.instantiateFn()
})
}
})
}
}
func (suite *KeeperTestSuite) TestInitializedPinnedCodes() {
var capturedChecksums []wasmvm.Checksum
testCases := []struct {
name string
malleate func()
expError error
}{
{
"success",
func() {
suite.mockVM.PinFn = func(checksum wasmvm.Checksum) error {
capturedChecksums = append(capturedChecksums, checksum)
return nil
}
},
nil,
},
{
"failure: pin error",
func() {
suite.mockVM.PinFn = func(checksum wasmvm.Checksum) error {
return wasmtesting.ErrMockVM
}
},
wasmtesting.ErrMockVM,
},
}
for _, tc := range testCases {
tc := tc
suite.Run(tc.name, func() {
suite.SetupWasmWithMockVM()
ctx := suite.chainA.GetContext()
wasmClientKeeper := GetSimApp(suite.chainA).WasmClientKeeper
contracts := [][]byte{wasmtesting.Code, wasmtesting.CreateMockContract([]byte("gzipped-contract"))}
checksumIDs := make([]types.Checksum, len(contracts))
signer := authtypes.NewModuleAddress(govtypes.ModuleName).String()
// store contract on chain
for i, contract := range contracts {
msg := types.NewMsgStoreCode(signer, contract)
res, err := wasmClientKeeper.StoreCode(ctx, msg)
suite.Require().NoError(err)
checksumIDs[i] = res.Checksum
}
// malleate after storing contracts
tc.malleate()
err := wasmClientKeeper.InitializePinnedCodes(ctx)
if tc.expError == nil {
suite.Require().NoError(err)
suite.ElementsMatch(checksumIDs, capturedChecksums)
} else {
suite.Require().ErrorIs(err, tc.expError)
}
})
}
}
func (suite *KeeperTestSuite) TestMigrateContract() {
var (
oldHash []byte
newHash []byte
payload []byte
expClientState *types.ClientState
)
testCases := []struct {
name string
malleate func()
expErr error
}{
{
"success: update client state",
func() {
suite.mockVM.MigrateFn = func(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ []byte, store wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error) {
expClientState = types.NewClientState([]byte{1}, newHash, clienttypes.NewHeight(2000, 2))
store.Set(host.ClientStateKey(), clienttypes.MustMarshalClientState(suite.chainA.App.AppCodec(), expClientState))
data, err := json.Marshal(types.EmptyResult{})
suite.Require().NoError(err)
return &wasmvmtypes.ContractResult{Ok: &wasmvmtypes.Response{Data: data}}, wasmtesting.DefaultGasUsed, nil
}
},
nil,
},
{
"failure: new and old checksum are the same",
func() {
newHash = oldHash
// this should not be called
suite.mockVM.MigrateFn = func(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ []byte, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error) {
panic("unreachable")
}
},
types.ErrWasmCodeExists,
},
{
"failure: checksum not found",
func() {
err := GetSimApp(suite.chainA).WasmClientKeeper.GetChecksums().Remove(suite.chainA.GetContext(), newHash)
suite.Require().NoError(err)
},
types.ErrWasmChecksumNotFound,
},
{
"failure: vm returns error",
func() {
err := GetSimApp(suite.chainA).WasmClientKeeper.GetChecksums().Set(suite.chainA.GetContext(), newHash)
suite.Require().NoError(err)
suite.mockVM.MigrateFn = func(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ []byte, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error) {
return nil, wasmtesting.DefaultGasUsed, wasmtesting.ErrMockVM
}
},
types.ErrVMError,
},
{
"failure: contract returns error",
func() {
err := GetSimApp(suite.chainA).WasmClientKeeper.GetChecksums().Set(suite.chainA.GetContext(), newHash)
suite.Require().NoError(err)
suite.mockVM.MigrateFn = func(_ wasmvm.Checksum, _ wasmvmtypes.Env, _ []byte, _ wasmvm.KVStore, _ wasmvm.GoAPI, _ wasmvm.Querier, _ wasmvm.GasMeter, _ uint64, _ wasmvmtypes.UFraction) (*wasmvmtypes.ContractResult, uint64, error) {
return &wasmvmtypes.ContractResult{Err: wasmtesting.ErrMockContract.Error()}, wasmtesting.DefaultGasUsed, nil
}
},
types.ErrWasmContractCallFailed,
},
}
for _, tc := range testCases {
tc := tc
suite.Run(tc.name, func() {
suite.SetupWasmWithMockVM()
suite.storeWasmCode(wasmtesting.Code)
var err error
oldHash, err = types.CreateChecksum(wasmtesting.Code)
suite.Require().NoError(err)
newHash, err = types.CreateChecksum(wasmtesting.CreateMockContract([]byte{1, 2, 3}))
suite.Require().NoError(err)
wasmClientKeeper := GetSimApp(suite.chainA).WasmClientKeeper
err = wasmClientKeeper.GetChecksums().Set(suite.chainA.GetContext(), newHash)
suite.Require().NoError(err)
endpointA := wasmtesting.NewWasmEndpoint(suite.chainA)
err = endpointA.CreateClient()
suite.Require().NoError(err)
clientState, ok := endpointA.GetClientState().(*types.ClientState)
suite.Require().True(ok)
expClientState = clientState
tc.malleate()
err = wasmClientKeeper.MigrateContractCode(suite.chainA.GetContext(), endpointA.ClientID, newHash, payload)
// updated client state
clientState, ok = endpointA.GetClientState().(*types.ClientState)
suite.Require().True(ok)
if tc.expErr == nil {
suite.Require().NoError(err)
suite.Require().Equal(expClientState, clientState)
} else {
suite.Require().ErrorIs(err, tc.expErr)
}
})
}
}
func (suite *KeeperTestSuite) TestGetChecksums() {
testCases := []struct {
name string
malleate func()
expResult func(checksums []types.Checksum)
}{
{
"success: no contract stored.",
func() {},
func(checksums []types.Checksum) {
suite.Require().Len(checksums, 0)
},
},
{
"success: default mock vm contract stored.",
func() {
suite.SetupWasmWithMockVM()
suite.storeWasmCode(wasmtesting.Code)
},
func(checksums []types.Checksum) {
suite.Require().Len(checksums, 1)
expectedChecksum, err := types.CreateChecksum(wasmtesting.Code)
suite.Require().NoError(err)
suite.Require().Equal(expectedChecksum, checksums[0])
},
},
{
"success: non-empty checksums",
func() {
suite.SetupWasmWithMockVM()
suite.storeWasmCode(wasmtesting.Code)
err := GetSimApp(suite.chainA).WasmClientKeeper.GetChecksums().Set(suite.chainA.GetContext(), types.Checksum("checksum"))
suite.Require().NoError(err)
},
func(checksums []types.Checksum) {
suite.Require().Len(checksums, 2)
suite.Require().Contains(checksums, types.Checksum("checksum"))
},
},
}
for _, tc := range testCases {
tc := tc
suite.Run(tc.name, func() {
tc.malleate()
checksums, err := GetSimApp(suite.chainA).WasmClientKeeper.GetAllChecksums(suite.chainA.GetContext())
suite.Require().NoError(err)
tc.expResult(checksums)
})
}
}
func (suite *KeeperTestSuite) TestAddChecksum() {
suite.SetupWasmWithMockVM()
suite.storeWasmCode(wasmtesting.Code)
wasmClientKeeper := GetSimApp(suite.chainA).WasmClientKeeper
checksums, err := wasmClientKeeper.GetAllChecksums(suite.chainA.GetContext())
suite.Require().NoError(err)
// default mock vm contract is stored
suite.Require().Len(checksums, 1)
checksum1 := types.Checksum("checksum1")
checksum2 := types.Checksum("checksum2")
err = wasmClientKeeper.GetChecksums().Set(suite.chainA.GetContext(), checksum1)
suite.Require().NoError(err)
err = wasmClientKeeper.GetChecksums().Set(suite.chainA.GetContext(), checksum2)
suite.Require().NoError(err)
// Test adding the same checksum twice
err = wasmClientKeeper.GetChecksums().Set(suite.chainA.GetContext(), checksum1)
suite.Require().NoError(err)
checksums, err = wasmClientKeeper.GetAllChecksums(suite.chainA.GetContext())
suite.Require().NoError(err)
suite.Require().Len(checksums, 3)
suite.Require().Contains(checksums, checksum1)
suite.Require().Contains(checksums, checksum2)
}
func (suite *KeeperTestSuite) TestHasChecksum() {
var checksum types.Checksum
testCases := []struct {
name string
malleate func()
exprResult bool
}{
{
"success: checksum exists",
func() {
checksum = types.Checksum("checksum")
err := GetSimApp(suite.chainA).WasmClientKeeper.GetChecksums().Set(suite.chainA.GetContext(), checksum)
suite.Require().NoError(err)
},
true,
},
{
"success: checksum does not exist",
func() {
checksum = types.Checksum("non-existent-checksum")
},
false,
},
}
for _, tc := range testCases {
tc := tc
suite.Run(tc.name, func() {
suite.SetupWasmWithMockVM()
tc.malleate()
result := GetSimApp(suite.chainA).WasmClientKeeper.HasChecksum(suite.chainA.GetContext(), checksum)
suite.Require().Equal(tc.exprResult, result)
})
}
}