github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/wasm/keeper/handler_plugin_test.go (about) 1 package keeper 2 3 import ( 4 "encoding/json" 5 "testing" 6 7 ibcadapter "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/ibc-adapter" 8 9 wasmvmtypes "github.com/CosmWasm/wasmvm/types" 10 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/baseapp" 11 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 12 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 13 capabilitytypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/capability/types" 14 clienttypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/02-client/types" 15 channeltypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/04-channel/types" 16 ibcexported "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/exported" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 20 "github.com/fibonacci-chain/fbc/x/wasm/keeper/wasmtesting" 21 "github.com/fibonacci-chain/fbc/x/wasm/types" 22 ) 23 24 func TestMessageHandlerChainDispatch(t *testing.T) { 25 capturingHandler, gotMsgs := wasmtesting.NewCapturingMessageHandler() 26 27 alwaysUnknownMsgHandler := &wasmtesting.MockMessageHandler{ 28 DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) { 29 return nil, nil, types.ErrUnknownMsg 30 }, 31 } 32 33 assertNotCalledHandler := &wasmtesting.MockMessageHandler{ 34 DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) { 35 t.Fatal("not expected to be called") 36 return 37 }, 38 } 39 40 myMsg := wasmvmtypes.CosmosMsg{Custom: []byte(`{}`)} 41 specs := map[string]struct { 42 handlers []Messenger 43 expErr *sdkerrors.Error 44 expEvents []sdk.Event 45 }{ 46 "single handler": { 47 handlers: []Messenger{capturingHandler}, 48 }, 49 "passed to next handler": { 50 handlers: []Messenger{alwaysUnknownMsgHandler, capturingHandler}, 51 }, 52 "stops iteration when handled": { 53 handlers: []Messenger{capturingHandler, assertNotCalledHandler}, 54 }, 55 "stops iteration on handler error": { 56 handlers: []Messenger{&wasmtesting.MockMessageHandler{ 57 DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) { 58 return nil, nil, types.ErrInvalidMsg 59 }, 60 }, assertNotCalledHandler}, 61 expErr: types.ErrInvalidMsg, 62 }, 63 "return events when handle": { 64 handlers: []Messenger{ 65 &wasmtesting.MockMessageHandler{ 66 DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) { 67 _, data, _ = capturingHandler.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg) 68 return []sdk.Event{sdk.NewEvent("myEvent", sdk.NewAttribute("foo", "bar"))}, data, nil 69 }, 70 }, 71 }, 72 expEvents: []sdk.Event{sdk.NewEvent("myEvent", sdk.NewAttribute("foo", "bar"))}, 73 }, 74 "return error when none can handle": { 75 handlers: []Messenger{alwaysUnknownMsgHandler}, 76 expErr: types.ErrUnknownMsg, 77 }, 78 } 79 for name, spec := range specs { 80 t.Run(name, func(t *testing.T) { 81 *gotMsgs = make([]wasmvmtypes.CosmosMsg, 0) 82 83 // when 84 h := MessageHandlerChain{spec.handlers} 85 gotEvents, gotData, gotErr := h.DispatchMsg(sdk.Context{}, RandomAccountAddress(t), "anyPort", myMsg) 86 87 // then 88 require.True(t, spec.expErr.Is(gotErr), "exp %v but got %#+v", spec.expErr, gotErr) 89 if spec.expErr != nil { 90 return 91 } 92 assert.Equal(t, []wasmvmtypes.CosmosMsg{myMsg}, *gotMsgs) 93 assert.Equal(t, [][]byte{{1}}, gotData) // {1} is default in capturing handler 94 assert.Equal(t, spec.expEvents, gotEvents) 95 }) 96 } 97 } 98 99 func TestSDKMessageHandlerDispatch(t *testing.T) { 100 myEvent := sdk.NewEvent("myEvent", sdk.NewAttribute("foo", "bar")) 101 const myData = "myData" 102 myRouterResult := sdk.Result{ 103 Data: []byte(myData), 104 Events: sdk.Events{myEvent}, 105 } 106 107 //var gotMsg []sdk.Msg 108 var gotMsg []string 109 capturingMessageRouter := wasmtesting.MessageRouterFunc(func(methodName string) baseapp.MsgServiceHandler { 110 return func(ctx sdk.Context, req sdk.MsgRequest) (*sdk.Result, error) { 111 gotMsg = append(gotMsg, methodName) 112 return &myRouterResult, nil 113 } 114 }) 115 noRouteMessageRouter := wasmtesting.MessageRouterFunc(func(methodName string) baseapp.MsgServiceHandler { 116 return nil 117 }) 118 myContractAddr := RandomAccountAddress(t) 119 myContractMessage := wasmvmtypes.CosmosMsg{Custom: []byte("{}")} 120 121 specs := map[string]struct { 122 srcRoute MessageRouter 123 srcEncoder CustomEncoder 124 expErr *sdkerrors.Error 125 expMsgDispatched int 126 }{ 127 "all good": { 128 srcRoute: capturingMessageRouter, 129 srcEncoder: func(sender sdk.AccAddress, msg json.RawMessage) ([]ibcadapter.Msg, error) { 130 myMsg := types.MsgExecuteContract{ 131 Sender: myContractAddr.String(), 132 Contract: RandomBech32AccountAddress(t), 133 Msg: []byte("{}"), 134 } 135 return []ibcadapter.Msg{&myMsg}, nil 136 }, 137 expMsgDispatched: 1, 138 }, 139 "multiple output msgs": { 140 srcRoute: capturingMessageRouter, 141 srcEncoder: func(sender sdk.AccAddress, msg json.RawMessage) ([]ibcadapter.Msg, error) { 142 first := &types.MsgExecuteContract{ 143 Sender: myContractAddr.String(), 144 Contract: RandomBech32AccountAddress(t), 145 Msg: []byte("{}"), 146 } 147 second := &types.MsgExecuteContract{ 148 Sender: myContractAddr.String(), 149 Contract: RandomBech32AccountAddress(t), 150 Msg: []byte("{}"), 151 } 152 return []ibcadapter.Msg{first, second}, nil 153 }, 154 expMsgDispatched: 2, 155 }, 156 "invalid sdk message rejected": { 157 srcRoute: capturingMessageRouter, 158 srcEncoder: func(sender sdk.AccAddress, msg json.RawMessage) ([]ibcadapter.Msg, error) { 159 invalidMsg := types.MsgExecuteContract{ 160 Sender: myContractAddr.String(), 161 Contract: RandomBech32AccountAddress(t), 162 Msg: []byte("INVALID_JSON"), 163 } 164 return []ibcadapter.Msg{&invalidMsg}, nil 165 }, 166 expErr: types.ErrInvalid, 167 }, 168 "invalid sender rejected": { 169 srcRoute: capturingMessageRouter, 170 srcEncoder: func(sender sdk.AccAddress, msg json.RawMessage) ([]ibcadapter.Msg, error) { 171 invalidMsg := types.MsgExecuteContract{ 172 Sender: RandomBech32AccountAddress(t), 173 Contract: RandomBech32AccountAddress(t), 174 Msg: []byte("{}"), 175 } 176 return []ibcadapter.Msg{&invalidMsg}, nil 177 }, 178 expErr: sdkerrors.ErrUnauthorized, 179 }, 180 "unroutable message rejected": { 181 srcRoute: noRouteMessageRouter, 182 srcEncoder: func(sender sdk.AccAddress, msg json.RawMessage) ([]ibcadapter.Msg, error) { 183 myMsg := types.MsgExecuteContract{ 184 Sender: myContractAddr.String(), 185 Contract: RandomBech32AccountAddress(t), 186 Msg: []byte("{}"), 187 } 188 return []ibcadapter.Msg{&myMsg}, nil 189 }, 190 expErr: sdkerrors.ErrUnknownRequest, 191 }, 192 "encoding error passed": { 193 srcRoute: capturingMessageRouter, 194 srcEncoder: func(sender sdk.AccAddress, msg json.RawMessage) ([]ibcadapter.Msg, error) { 195 myErr := types.ErrUnpinContractFailed // any error that is not used 196 return nil, myErr 197 }, 198 expErr: types.ErrUnpinContractFailed, 199 }, 200 } 201 for name, spec := range specs { 202 t.Run(name, func(t *testing.T) { 203 //gotMsg = make([]sdk.Msg, 0) 204 gotMsg = make([]string, 0) 205 206 // when 207 ctx := sdk.Context{} 208 h := NewSDKMessageHandler(spec.srcRoute, MessageEncoders{Custom: spec.srcEncoder}) 209 gotEvents, gotData, gotErr := h.DispatchMsg(ctx, myContractAddr, "myPort", myContractMessage) 210 211 // then 212 require.True(t, spec.expErr.Is(gotErr), "exp %v but got %#+v", spec.expErr, gotErr) 213 if spec.expErr != nil { 214 require.Len(t, gotMsg, 0) 215 return 216 } 217 assert.Len(t, gotMsg, spec.expMsgDispatched) 218 for i := 0; i < spec.expMsgDispatched; i++ { 219 assert.Equal(t, myEvent, gotEvents[i]) 220 assert.Equal(t, []byte(myData), gotData[i]) 221 } 222 }) 223 } 224 } 225 226 func TestIBCRawPacketHandler(t *testing.T) { 227 ibcPort := "contractsIBCPort" 228 var ctx sdk.Context 229 230 var capturedPacket ibcexported.PacketI 231 232 chanKeeper := &wasmtesting.MockChannelKeeper{ 233 GetNextSequenceSendFn: func(ctx sdk.Context, portID, channelID string) (uint64, bool) { 234 return 1, true 235 }, 236 GetChannelFn: func(ctx sdk.Context, srcPort, srcChan string) (channeltypes.Channel, bool) { 237 return channeltypes.Channel{ 238 Counterparty: channeltypes.NewCounterparty( 239 "other-port", 240 "other-channel-1", 241 ), 242 }, true 243 }, 244 SendPacketFn: func(ctx sdk.Context, channelCap *capabilitytypes.Capability, packet ibcexported.PacketI) error { 245 capturedPacket = packet 246 return nil 247 }, 248 } 249 capKeeper := &wasmtesting.MockCapabilityKeeper{ 250 GetCapabilityFn: func(ctx sdk.Context, name string) (*capabilitytypes.Capability, bool) { 251 return &capabilitytypes.Capability{}, true 252 }, 253 } 254 255 specs := map[string]struct { 256 srcMsg wasmvmtypes.SendPacketMsg 257 chanKeeper types.ChannelKeeper 258 capKeeper types.CapabilityKeeper 259 expPacketSent channeltypes.Packet 260 expErr *sdkerrors.Error 261 }{ 262 "all good": { 263 srcMsg: wasmvmtypes.SendPacketMsg{ 264 ChannelID: "channel-1", 265 Data: []byte("myData"), 266 Timeout: wasmvmtypes.IBCTimeout{Block: &wasmvmtypes.IBCTimeoutBlock{Revision: 1, Height: 2}}, 267 }, 268 chanKeeper: chanKeeper, 269 capKeeper: capKeeper, 270 expPacketSent: channeltypes.Packet{ 271 Sequence: 1, 272 SourcePort: ibcPort, 273 SourceChannel: "channel-1", 274 DestinationPort: "other-port", 275 DestinationChannel: "other-channel-1", 276 Data: []byte("myData"), 277 TimeoutHeight: clienttypes.Height{RevisionNumber: 1, RevisionHeight: 2}, 278 }, 279 }, 280 "sequence not found returns error": { 281 srcMsg: wasmvmtypes.SendPacketMsg{ 282 ChannelID: "channel-1", 283 Data: []byte("myData"), 284 Timeout: wasmvmtypes.IBCTimeout{Block: &wasmvmtypes.IBCTimeoutBlock{Revision: 1, Height: 2}}, 285 }, 286 chanKeeper: &wasmtesting.MockChannelKeeper{ 287 GetNextSequenceSendFn: func(ctx sdk.Context, portID, channelID string) (uint64, bool) { 288 return 0, false 289 }, 290 }, 291 expErr: channeltypes.ErrSequenceSendNotFound, 292 }, 293 "capability not found returns error": { 294 srcMsg: wasmvmtypes.SendPacketMsg{ 295 ChannelID: "channel-1", 296 Data: []byte("myData"), 297 Timeout: wasmvmtypes.IBCTimeout{Block: &wasmvmtypes.IBCTimeoutBlock{Revision: 1, Height: 2}}, 298 }, 299 chanKeeper: chanKeeper, 300 capKeeper: wasmtesting.MockCapabilityKeeper{ 301 GetCapabilityFn: func(ctx sdk.Context, name string) (*capabilitytypes.Capability, bool) { 302 return nil, false 303 }, 304 }, 305 expErr: channeltypes.ErrChannelCapabilityNotFound, 306 }, 307 } 308 for name, spec := range specs { 309 t.Run(name, func(t *testing.T) { 310 capturedPacket = nil 311 // when 312 h := NewIBCRawPacketHandler(spec.chanKeeper, spec.capKeeper) 313 data, evts, gotErr := h.DispatchMsg(ctx, RandomAccountAddress(t), ibcPort, wasmvmtypes.CosmosMsg{IBC: &wasmvmtypes.IBCMsg{SendPacket: &spec.srcMsg}}) 314 // then 315 require.True(t, spec.expErr.Is(gotErr), "exp %v but got %#+v", spec.expErr, gotErr) 316 if spec.expErr != nil { 317 return 318 } 319 assert.Nil(t, data) 320 assert.Nil(t, evts) 321 assert.Equal(t, spec.expPacketSent, capturedPacket) 322 }) 323 } 324 } 325 326 //func TestBurnCoinMessageHandlerIntegration(t *testing.T) { 327 // // testing via full keeper setup so that we are confident the 328 // // module permissions are set correct and no other handler 329 // // picks the message in the default handler chain 330 // ctx, keepers := CreateDefaultTestInput(t) 331 // // set some supply 332 // keepers.Faucet.NewFundedAccount(ctx, sdk.NewCoin("denom", sdk.NewInt(10_000_000))) 333 // k := keepers.WasmKeeper 334 // 335 // example := InstantiateHackatomExampleContract(t, ctx, keepers) // with deposit of 100 stake 336 // 337 // before, err := keepers.BankKeeper.TotalSupply(sdk.WrapSDKContext(ctx), &banktypes.QueryTotalSupplyRequest{}) 338 // require.NoError(t, err) 339 // 340 // specs := map[string]struct { 341 // msg wasmvmtypes.BurnMsg 342 // expErr bool 343 // }{ 344 // "all good": { 345 // msg: wasmvmtypes.BurnMsg{ 346 // Amount: wasmvmtypes.Coins{{ 347 // Denom: "denom", 348 // Amount: "100", 349 // }}, 350 // }, 351 // }, 352 // "not enough funds in contract": { 353 // msg: wasmvmtypes.BurnMsg{ 354 // Amount: wasmvmtypes.Coins{{ 355 // Denom: "denom", 356 // Amount: "101", 357 // }}, 358 // }, 359 // expErr: true, 360 // }, 361 // "zero amount rejected": { 362 // msg: wasmvmtypes.BurnMsg{ 363 // Amount: wasmvmtypes.Coins{{ 364 // Denom: "denom", 365 // Amount: "0", 366 // }}, 367 // }, 368 // expErr: true, 369 // }, 370 // "unknown denom - insufficient funds": { 371 // msg: wasmvmtypes.BurnMsg{ 372 // Amount: wasmvmtypes.Coins{{ 373 // Denom: "unknown", 374 // Amount: "1", 375 // }}, 376 // }, 377 // expErr: true, 378 // }, 379 // } 380 // parentCtx := ctx 381 // for name, spec := range specs { 382 // t.Run(name, func(t *testing.T) { 383 // ctx, _ = parentCtx.CacheContext() 384 // k.wasmVM = &wasmtesting.MockWasmer{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) { 385 // return &wasmvmtypes.Response{ 386 // Messages: []wasmvmtypes.SubMsg{ 387 // {Msg: wasmvmtypes.CosmosMsg{Bank: &wasmvmtypes.BankMsg{Burn: &spec.msg}}, ReplyOn: wasmvmtypes.ReplyNever}, 388 // }, 389 // }, 0, nil 390 // }} 391 // 392 // // when 393 // _, err = k.execute(ctx, example.Contract, example.CreatorAddr, nil, nil) 394 // 395 // // then 396 // if spec.expErr { 397 // require.Error(t, err) 398 // return 399 // } 400 // require.NoError(t, err) 401 // 402 // // and total supply reduced by burned amount 403 // after, err := keepers.BankKeeper.TotalSupply(sdk.WrapSDKContext(ctx), &banktypes.QueryTotalSupplyRequest{}) 404 // require.NoError(t, err) 405 // diff := before.Supply.Sub(after.Supply) 406 // assert.Equal(t, sdk.NewCoins(sdk.NewCoin("denom", sdk.NewInt(100))), diff) 407 // }) 408 // } 409 // 410 // // test cases: 411 // // not enough money to burn 412 //}