From d14d7fbe8b4c56341da3cae29d5de78e038d7b67 Mon Sep 17 00:00:00 2001 From: codchen Date: Fri, 14 Jun 2024 05:11:22 +0800 Subject: [PATCH 1/6] Allow passing ctx in return values of submsg handler (#52) --- x/wasm/keeper/handler_plugin.go | 68 +++++++++---------- x/wasm/keeper/handler_plugin_test.go | 31 +++++---- x/wasm/keeper/keeper.go | 18 +++++- x/wasm/keeper/msg_dispatcher.go | 14 ++-- x/wasm/keeper/msg_dispatcher_test.go | 72 ++++++++++----------- x/wasm/keeper/wasmtesting/message_router.go | 11 ++-- x/wasm/keeper/wasmtesting/messenger.go | 12 ++-- 7 files changed, 121 insertions(+), 105 deletions(-) diff --git a/x/wasm/keeper/handler_plugin.go b/x/wasm/keeper/handler_plugin.go index 17c156c..81141a2 100644 --- a/x/wasm/keeper/handler_plugin.go +++ b/x/wasm/keeper/handler_plugin.go @@ -5,7 +5,6 @@ import ( "fmt" wasmvmtypes "github.com/CosmWasm/wasmvm/types" - "github.com/cosmos/cosmos-sdk/baseapp" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -21,9 +20,11 @@ type msgEncoder interface { Encode(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) ([]sdk.Msg, error) } +type MsgHandler = func(ctx sdk.Context, req sdk.Msg) (sdk.Context, *sdk.Result, error) + // MessageRouter ADR 031 request type routing type MessageRouter interface { - Handler(msg sdk.Msg) baseapp.MsgServiceHandler + Handler(msg sdk.Msg) MsgHandler } // SDKMessageHandler can handles messages that can be encoded into sdk.Message types and routed. @@ -59,16 +60,17 @@ func NewSDKMessageHandler(router MessageRouter, encoders msgEncoder) SDKMessageH } } -func (h SDKMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { +func (h SDKMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { sdkMsgs, err := h.encoders.Encode(ctx, contractAddr, contractIBCPortID, msg, info, codeInfo) if err != nil { - return nil, nil, err + return ctx, nil, nil, err } for _, sdkMsg := range sdkMsgs { - res, err := h.handleSdkMessage(ctx, contractAddr, sdkMsg) + resCtx, res, err := h.handleSdkMessage(ctx, contractAddr, sdkMsg) if err != nil { - return nil, nil, err + return ctx, nil, nil, err } + ctx = resCtx // append data data = append(data, res.Data) // append events @@ -81,29 +83,29 @@ func (h SDKMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddr return } -func (h SDKMessageHandler) handleSdkMessage(ctx sdk.Context, contractAddr sdk.Address, msg sdk.Msg) (*sdk.Result, error) { +func (h SDKMessageHandler) handleSdkMessage(ctx sdk.Context, contractAddr sdk.Address, msg sdk.Msg) (sdk.Context, *sdk.Result, error) { if err := msg.ValidateBasic(); err != nil { - return nil, err + return ctx, nil, err } // make sure this account can send it for _, acct := range msg.GetSigners() { if !acct.Equals(contractAddr) { - return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "contract doesn't have permission") + return ctx, nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "contract doesn't have permission") } } // find the handler and execute it if handler := h.router.Handler(msg); handler != nil { // ADR 031 request type routing - msgResult, err := handler(ctx, msg) - return msgResult, err + resCtx, msgResult, err := handler(ctx, msg) + return resCtx, msgResult, err } // legacy sdk.Msg routing // Assuming that the app developer has migrated all their Msgs to // proto messages and has registered all `Msg services`, then this // path should never be called, because all those Msgs should be // registered within the `msgServiceRouter` already. - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "can't route message %+v", msg) + return ctx, nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "can't route message %+v", msg) } // MessageHandlerChain defines a chain of handlers that are called one by one until it can be handled. @@ -125,19 +127,19 @@ func NewMessageHandlerChain(first Messenger, others ...Messenger) *MessageHandle // order to find the right one to process given message. If a handler cannot // process given message (returns ErrUnknownMsg), its result is ignored and the // next handler is executed. -func (m MessageHandlerChain) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) ([]sdk.Event, [][]byte, error) { +func (m MessageHandlerChain) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (sdk.Context, []sdk.Event, [][]byte, error) { for _, h := range m.handlers { - events, data, err := h.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg, info, codeInfo) + resCtx, events, data, err := h.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg, info, codeInfo) switch { case err == nil: - return events, data, nil + return resCtx, events, data, nil case errors.Is(err, types.ErrUnknownMsg): continue default: - return events, data, err + return ctx, events, data, err } } - return nil, nil, sdkerrors.Wrap(types.ErrUnknownMsg, "no handler found") + return ctx, nil, nil, sdkerrors.Wrap(types.ErrUnknownMsg, "no handler found") } // IBCRawPacketHandler handels IBC.SendPacket messages which are published to an IBC channel. @@ -151,32 +153,32 @@ func NewIBCRawPacketHandler(chk types.ChannelKeeper, cak types.CapabilityKeeper) } // DispatchMsg publishes a raw IBC packet onto the channel. -func (h IBCRawPacketHandler) DispatchMsg(ctx sdk.Context, _ sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, _ wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { +func (h IBCRawPacketHandler) DispatchMsg(ctx sdk.Context, _ sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, _ wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { if msg.IBC == nil || msg.IBC.SendPacket == nil { - return nil, nil, types.ErrUnknownMsg + return ctx, nil, nil, types.ErrUnknownMsg } if contractIBCPortID == "" { - return nil, nil, sdkerrors.Wrapf(types.ErrUnsupportedForContract, "ibc not supported") + return ctx, nil, nil, sdkerrors.Wrapf(types.ErrUnsupportedForContract, "ibc not supported") } contractIBCChannelID := msg.IBC.SendPacket.ChannelID if contractIBCChannelID == "" { - return nil, nil, sdkerrors.Wrapf(types.ErrEmpty, "ibc channel") + return ctx, nil, nil, sdkerrors.Wrapf(types.ErrEmpty, "ibc channel") } sequence, found := h.channelKeeper.GetNextSequenceSend(ctx, contractIBCPortID, contractIBCChannelID) if !found { - return nil, nil, sdkerrors.Wrapf(channeltypes.ErrSequenceSendNotFound, + return ctx, nil, nil, sdkerrors.Wrapf(channeltypes.ErrSequenceSendNotFound, "source port: %s, source channel: %s", contractIBCPortID, contractIBCChannelID, ) } channelInfo, ok := h.channelKeeper.GetChannel(ctx, contractIBCPortID, contractIBCChannelID) if !ok { - return nil, nil, sdkerrors.Wrap(channeltypes.ErrInvalidChannel, "not found") + return ctx, nil, nil, sdkerrors.Wrap(channeltypes.ErrInvalidChannel, "not found") } channelCap, ok := h.capabilityKeeper.GetCapability(ctx, host.ChannelCapabilityPath(contractIBCPortID, contractIBCChannelID)) if !ok { - return nil, nil, sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") + return ctx, nil, nil, sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") } packet := channeltypes.NewPacket( msg.IBC.SendPacket.Data, @@ -188,36 +190,36 @@ func (h IBCRawPacketHandler) DispatchMsg(ctx sdk.Context, _ sdk.AccAddress, cont ConvertWasmIBCTimeoutHeightToCosmosHeight(msg.IBC.SendPacket.Timeout.Block), msg.IBC.SendPacket.Timeout.Timestamp, ) - return nil, nil, h.channelKeeper.SendPacket(ctx, channelCap, packet) + return ctx, nil, nil, h.channelKeeper.SendPacket(ctx, channelCap, packet) } var _ Messenger = MessageHandlerFunc(nil) // MessageHandlerFunc is a helper to construct a function based message handler. -type MessageHandlerFunc func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (events []sdk.Event, data [][]byte, err error) +type MessageHandlerFunc func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) // DispatchMsg delegates dispatching of provided message into the MessageHandlerFunc. -func (m MessageHandlerFunc) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { +func (m MessageHandlerFunc) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { return m(ctx, contractAddr, contractIBCPortID, msg, info, codeInfo) } // NewBurnCoinMessageHandler handles wasmvm.BurnMsg messages func NewBurnCoinMessageHandler(burner types.Burner) MessageHandlerFunc { - return func(ctx sdk.Context, contractAddr sdk.AccAddress, _ string, msg wasmvmtypes.CosmosMsg, _ wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { + return func(ctx sdk.Context, contractAddr sdk.AccAddress, _ string, msg wasmvmtypes.CosmosMsg, _ wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { if msg.Bank != nil && msg.Bank.Burn != nil { coins, err := ConvertWasmCoinsToSdkCoins(msg.Bank.Burn.Amount) if err != nil { - return nil, nil, err + return ctx, nil, nil, err } if err := burner.SendCoinsFromAccountToModule(ctx, contractAddr, types.ModuleName, coins); err != nil { - return nil, nil, sdkerrors.Wrap(err, "transfer to module") + return ctx, nil, nil, sdkerrors.Wrap(err, "transfer to module") } if err := burner.BurnCoins(ctx, types.ModuleName, coins); err != nil { - return nil, nil, sdkerrors.Wrap(err, "burn coins") + return ctx, nil, nil, sdkerrors.Wrap(err, "burn coins") } moduleLogger(ctx).Info("Burned", "amount", coins) - return nil, nil, nil + return ctx, nil, nil, nil } - return nil, nil, types.ErrUnknownMsg + return ctx, nil, nil, types.ErrUnknownMsg } } diff --git a/x/wasm/keeper/handler_plugin_test.go b/x/wasm/keeper/handler_plugin_test.go index 0681014..27e50ec 100644 --- a/x/wasm/keeper/handler_plugin_test.go +++ b/x/wasm/keeper/handler_plugin_test.go @@ -6,7 +6,6 @@ import ( wasmvm "github.com/CosmWasm/wasmvm" wasmvmtypes "github.com/CosmWasm/wasmvm/types" - "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -25,13 +24,13 @@ func TestMessageHandlerChainDispatch(t *testing.T) { capturingHandler, gotMsgs := wasmtesting.NewCapturingMessageHandler() alwaysUnknownMsgHandler := &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { - return nil, nil, types.ErrUnknownMsg + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { + return ctx, nil, nil, types.ErrUnknownMsg }, } assertNotCalledHandler := &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { t.Fatal("not expected to be called") return }, @@ -54,8 +53,8 @@ func TestMessageHandlerChainDispatch(t *testing.T) { }, "stops iteration on handler error": { handlers: []Messenger{&wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { - return nil, nil, types.ErrInvalidMsg + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { + return ctx, nil, nil, types.ErrInvalidMsg }, }, assertNotCalledHandler}, expErr: types.ErrInvalidMsg, @@ -63,9 +62,9 @@ func TestMessageHandlerChainDispatch(t *testing.T) { "return events when handle": { handlers: []Messenger{ &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { - _, data, _ = capturingHandler.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg, info, codeInfo) - return []sdk.Event{sdk.NewEvent("myEvent", sdk.NewAttribute("foo", "bar"))}, data, nil + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { + resCtx, _, data, _ = capturingHandler.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg, info, codeInfo) + return resCtx, []sdk.Event{sdk.NewEvent("myEvent", sdk.NewAttribute("foo", "bar"))}, data, nil }, }, }, @@ -82,7 +81,7 @@ func TestMessageHandlerChainDispatch(t *testing.T) { // when h := MessageHandlerChain{spec.handlers} - gotEvents, gotData, gotErr := h.DispatchMsg(sdk.Context{}, RandomAccountAddress(t), "anyPort", myMsg, wasmvmtypes.MessageInfo{}, types.CodeInfo{}) + _, gotEvents, gotData, gotErr := h.DispatchMsg(sdk.Context{}, RandomAccountAddress(t), "anyPort", myMsg, wasmvmtypes.MessageInfo{}, types.CodeInfo{}) // then require.True(t, spec.expErr.Is(gotErr), "exp %v but got %#+v", spec.expErr, gotErr) @@ -105,13 +104,13 @@ func TestSDKMessageHandlerDispatch(t *testing.T) { } var gotMsg []sdk.Msg - capturingMessageRouter := wasmtesting.MessageRouterFunc(func(msg sdk.Msg) baseapp.MsgServiceHandler { - return func(ctx sdk.Context, req sdk.Msg) (*sdk.Result, error) { + capturingMessageRouter := wasmtesting.MessageRouterFunc(func(msg sdk.Msg) MsgHandler { + return func(ctx sdk.Context, req sdk.Msg) (sdk.Context, *sdk.Result, error) { gotMsg = append(gotMsg, msg) - return &myRouterResult, nil + return ctx, &myRouterResult, nil } }) - noRouteMessageRouter := wasmtesting.MessageRouterFunc(func(msg sdk.Msg) baseapp.MsgServiceHandler { + noRouteMessageRouter := wasmtesting.MessageRouterFunc(func(msg sdk.Msg) MsgHandler { return nil }) myContractAddr := RandomAccountAddress(t) @@ -204,7 +203,7 @@ func TestSDKMessageHandlerDispatch(t *testing.T) { // when ctx := sdk.Context{} h := NewSDKMessageHandler(spec.srcRoute, MessageEncoders{Custom: spec.srcEncoder}) - gotEvents, gotData, gotErr := h.DispatchMsg(ctx, myContractAddr, "myPort", myContractMessage, wasmvmtypes.MessageInfo{}, types.CodeInfo{}) + _, gotEvents, gotData, gotErr := h.DispatchMsg(ctx, myContractAddr, "myPort", myContractMessage, wasmvmtypes.MessageInfo{}, types.CodeInfo{}) // then require.True(t, spec.expErr.Is(gotErr), "exp %v but got %#+v", spec.expErr, gotErr) @@ -308,7 +307,7 @@ func TestIBCRawPacketHandler(t *testing.T) { capturedPacket = nil // when h := NewIBCRawPacketHandler(spec.chanKeeper, spec.capKeeper) - data, evts, gotErr := h.DispatchMsg(ctx, RandomAccountAddress(t), ibcPort, wasmvmtypes.CosmosMsg{IBC: &wasmvmtypes.IBCMsg{SendPacket: &spec.srcMsg}}, wasmvmtypes.MessageInfo{}, types.CodeInfo{}) + _, data, evts, gotErr := h.DispatchMsg(ctx, RandomAccountAddress(t), ibcPort, wasmvmtypes.CosmosMsg{IBC: &wasmvmtypes.IBCMsg{SendPacket: &spec.srcMsg}}, wasmvmtypes.MessageInfo{}, types.CodeInfo{}) // then require.True(t, spec.expErr.Is(gotErr), "exp %v but got %#+v", spec.expErr, gotErr) if spec.expErr != nil { diff --git a/x/wasm/keeper/keeper.go b/x/wasm/keeper/keeper.go index fd4cd12..ac59ee4 100644 --- a/x/wasm/keeper/keeper.go +++ b/x/wasm/keeper/keeper.go @@ -12,6 +12,7 @@ import ( "time" "github.com/armon/go-metrics" + "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/types/address" wasmvm "github.com/CosmWasm/wasmvm" @@ -89,6 +90,18 @@ type Keeper struct { maxQueryStackSize uint32 } +type routerWithContext struct { + router *baseapp.MsgServiceRouter +} + +func (rc routerWithContext) Handler(msg sdk.Msg) MsgHandler { + h := rc.router.Handler(msg) + return func(ctx sdk.Context, req sdk.Msg) (sdk.Context, *sdk.Result, error) { + result, err := h(ctx, msg) + return ctx, result, err + } +} + // NewKeeper creates a new contract Keeper instance // If customEncoders is non-nil, we can use this to override some of the message handler, especially custom func NewKeeper( @@ -104,7 +117,7 @@ func NewKeeper( portKeeper types.PortKeeper, capabilityKeeper types.CapabilityKeeper, portSource types.ICS20TransferPortSource, - router MessageRouter, + router *baseapp.MsgServiceRouter, queryRouter GRPCQueryRouter, homeDir string, wasmConfig types.WasmConfig, @@ -120,6 +133,7 @@ func NewKeeper( paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) } + routerWithCtx := routerWithContext{router} keeper := &Keeper{ storeKey: storeKey, cdc: cdc, @@ -129,7 +143,7 @@ func NewKeeper( bank: NewBankCoinTransferrer(bankKeeper), portKeeper: portKeeper, capabilityKeeper: capabilityKeeper, - messenger: NewDefaultMessageHandler(router, channelKeeper, capabilityKeeper, bankKeeper, cdc, portSource), + messenger: NewDefaultMessageHandler(routerWithCtx, channelKeeper, capabilityKeeper, bankKeeper, cdc, portSource), queryGasLimit: wasmConfig.SmartQueryGasLimit, paramSpace: paramSpace, gasRegister: NewDefaultWasmGasRegister(), diff --git a/x/wasm/keeper/msg_dispatcher.go b/x/wasm/keeper/msg_dispatcher.go index 89c587e..d1980b1 100644 --- a/x/wasm/keeper/msg_dispatcher.go +++ b/x/wasm/keeper/msg_dispatcher.go @@ -14,7 +14,7 @@ import ( // Messenger is an extension point for custom wasmd message handling type Messenger interface { // DispatchMsg encodes the wasmVM message and dispatches it. - DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (events []sdk.Event, data [][]byte, err error) + DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) } // replyer is a subset of keeper that can handle replies to submessages @@ -36,7 +36,7 @@ func NewMessageDispatcher(messenger Messenger, keeper replyer) *MessageDispatche // DispatchMessages sends all messages. func (d MessageDispatcher) DispatchMessages(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, msgs []wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) error { for _, msg := range msgs { - events, _, err := d.messenger.DispatchMsg(ctx, contractAddr, ibcPort, msg, info, codeInfo) + _, events, _, err := d.messenger.DispatchMsg(ctx, contractAddr, ibcPort, msg, info, codeInfo) if err != nil { return err } @@ -47,7 +47,7 @@ func (d MessageDispatcher) DispatchMessages(ctx sdk.Context, contractAddr sdk.Ac } // dispatchMsgWithGasLimit sends a message with gas limit applied -func (d MessageDispatcher) dispatchMsgWithGasLimit(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, msg wasmvmtypes.CosmosMsg, gasLimit uint64, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { +func (d MessageDispatcher) dispatchMsgWithGasLimit(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, msg wasmvmtypes.CosmosMsg, gasLimit uint64, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { limitedMeter := sdk.NewGasMeterWithMultiplier(ctx, gasLimit) subCtx := ctx.WithGasMeter(limitedMeter) @@ -64,13 +64,13 @@ func (d MessageDispatcher) dispatchMsgWithGasLimit(ctx sdk.Context, contractAddr err = sdkerrors.Wrap(sdkerrors.ErrOutOfGas, "SubMsg hit gas limit") } }() - events, data, err = d.messenger.DispatchMsg(subCtx, contractAddr, ibcPort, msg, info, codeInfo) + resCtx, events, data, err = d.messenger.DispatchMsg(subCtx, contractAddr, ibcPort, msg, info, codeInfo) // make sure we charge the parent what was spent spent := subCtx.GasMeter().GasConsumed() ctx.GasMeter().ConsumeGas(spent, "From limited Sub-Message") - return events, data, err + return resCtx, events, data, err } // DispatchSubmessages builds a sandbox to execute these messages and returns the execution result to the contract @@ -96,9 +96,9 @@ func (d MessageDispatcher) DispatchSubmessages(ctx sdk.Context, contractAddr sdk var events []sdk.Event var data [][]byte if limitGas { - events, data, err = d.dispatchMsgWithGasLimit(subCtx, contractAddr, ibcPort, msg.Msg, *msg.GasLimit, info, codeInfo) + ctx, events, data, err = d.dispatchMsgWithGasLimit(subCtx, contractAddr, ibcPort, msg.Msg, *msg.GasLimit, info, codeInfo) } else { - events, data, err = d.messenger.DispatchMsg(subCtx, contractAddr, ibcPort, msg.Msg, info, codeInfo) + ctx, events, data, err = d.messenger.DispatchMsg(subCtx, contractAddr, ibcPort, msg.Msg, info, codeInfo) } // if it succeeds, commit state changes from submessage, and pass on events to Event Manager diff --git a/x/wasm/keeper/msg_dispatcher_test.go b/x/wasm/keeper/msg_dispatcher_test.go index c1a4d0a..34395c3 100644 --- a/x/wasm/keeper/msg_dispatcher_test.go +++ b/x/wasm/keeper/msg_dispatcher_test.go @@ -33,8 +33,8 @@ func TestDispatchSubmessages(t *testing.T) { msgs: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplyError}}, replyer: noReplyCalled, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { - return nil, [][]byte{[]byte("myData")}, nil + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { + return ctx, nil, [][]byte{[]byte("myData")}, nil }, }, expCommits: []bool{true}, @@ -43,8 +43,8 @@ func TestDispatchSubmessages(t *testing.T) { msgs: []wasmvmtypes.SubMsg{{ReplyOn: wasmvmtypes.ReplySuccess}}, replyer: noReplyCalled, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { - return nil, nil, errors.New("test, ignore") + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { + return ctx, nil, nil, errors.New("test, ignore") }, }, expCommits: []bool{false}, @@ -60,8 +60,8 @@ func TestDispatchSubmessages(t *testing.T) { }, }, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { - return nil, [][]byte{[]byte("myData")}, nil + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { + return ctx, nil, [][]byte{[]byte("myData")}, nil }, }, expData: []byte("myReplyData"), @@ -77,8 +77,8 @@ func TestDispatchSubmessages(t *testing.T) { }, }, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { - return nil, nil, errors.New("my error") + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { + return ctx, nil, nil, errors.New("my error") }, }, expData: []byte("myReplyData"), @@ -95,9 +95,9 @@ func TestDispatchSubmessages(t *testing.T) { }, }, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { myEvents := []sdk.Event{{Type: "myEvent", Attributes: []abci.EventAttribute{{Key: []byte("foo"), Value: []byte("bar")}}}} - return myEvents, [][]byte{[]byte("myData")}, nil + return ctx, myEvents, [][]byte{[]byte("myData")}, nil }, }, expData: []byte("myReplyData"), @@ -116,10 +116,10 @@ func TestDispatchSubmessages(t *testing.T) { }}, replyer: &mockReplyer{}, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { myEvents := []sdk.Event{{Type: "myEvent", Attributes: []abci.EventAttribute{{Key: []byte("foo"), Value: []byte("bar")}}}} ctx.EventManager().EmitEvents(myEvents) - return nil, nil, nil + return ctx, nil, nil, nil }, }, expCommits: []bool{true}, @@ -134,10 +134,10 @@ func TestDispatchSubmessages(t *testing.T) { }}, replyer: &mockReplyer{}, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { myEvents := []sdk.Event{{Type: "myEvent", Attributes: []abci.EventAttribute{{Key: []byte("foo"), Value: []byte("bar")}}}} ctx.EventManager().EmitEvents(myEvents) - return nil, nil, errors.New("testing") + return ctx, nil, nil, errors.New("testing") }, }, expCommits: []bool{false}, @@ -153,8 +153,8 @@ func TestDispatchSubmessages(t *testing.T) { }, }, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { - return nil, nil, nil + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { + return ctx, nil, nil, nil }, }, expCommits: []bool{false}, @@ -171,9 +171,9 @@ func TestDispatchSubmessages(t *testing.T) { }, }, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { ctx.GasMeter().ConsumeGas(sdk.Gas(101), "testing") - return nil, [][]byte{[]byte("someData")}, nil + return ctx, nil, [][]byte{[]byte("someData")}, nil }, }, expData: []byte("myReplyData"), @@ -186,9 +186,9 @@ func TestDispatchSubmessages(t *testing.T) { }}, replyer: &mockReplyer{}, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { ctx.GasMeter().ConsumeGas(sdk.Gas(1), "testing") - return nil, [][]byte{[]byte("someData")}, nil + return ctx, nil, [][]byte{[]byte("someData")}, nil }, }, expCommits: []bool{true}, @@ -197,8 +197,8 @@ func TestDispatchSubmessages(t *testing.T) { msgs: []wasmvmtypes.SubMsg{{ID: 1, ReplyOn: wasmvmtypes.ReplyNever}, {ID: 2, ReplyOn: wasmvmtypes.ReplyNever}}, replyer: &mockReplyer{}, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { - return nil, [][]byte{nil}, nil + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { + return ctx, nil, [][]byte{nil}, nil }, }, expCommits: []bool{true, true}, @@ -207,8 +207,8 @@ func TestDispatchSubmessages(t *testing.T) { msgs: []wasmvmtypes.SubMsg{{ID: 1, ReplyOn: wasmvmtypes.ReplyNever}, {ID: 2, ReplyOn: wasmvmtypes.ReplyNever}}, replyer: &mockReplyer{}, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { - return nil, [][]byte{{}}, nil + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { + return ctx, nil, [][]byte{{}}, nil }, }, expCommits: []bool{true, true}, @@ -217,8 +217,8 @@ func TestDispatchSubmessages(t *testing.T) { msgs: []wasmvmtypes.SubMsg{{ID: 1, ReplyOn: wasmvmtypes.ReplyNever}, {ID: 2, ReplyOn: wasmvmtypes.ReplyNever}}, replyer: &mockReplyer{}, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { - return nil, [][]byte{{}}, errors.New("testing") + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { + return ctx, nil, [][]byte{{}}, errors.New("testing") }, }, expCommits: []bool{false, false}, @@ -232,8 +232,8 @@ func TestDispatchSubmessages(t *testing.T) { }, }, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { - return nil, nil, errors.New("my error") + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { + return ctx, nil, nil, errors.New("my error") }, }, expData: []byte("myReplyData:2"), @@ -250,8 +250,8 @@ func TestDispatchSubmessages(t *testing.T) { }, }, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { - return nil, nil, errors.New("my error") + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { + return ctx, nil, nil, errors.New("my error") }, }, expData: []byte("myReplyData:1"), @@ -268,8 +268,8 @@ func TestDispatchSubmessages(t *testing.T) { }, }, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { - return nil, nil, errors.New("my error") + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { + return ctx, nil, nil, errors.New("my error") }, }, expData: []byte{}, @@ -285,12 +285,12 @@ func TestDispatchSubmessages(t *testing.T) { }, }, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { myEvents := []sdk.Event{ sdk.NewEvent("message"), sdk.NewEvent("execute", sdk.NewAttribute("foo", "bar")), } - return myEvents, [][]byte{[]byte("myData")}, nil + return ctx, myEvents, [][]byte{[]byte("myData")}, nil }, }, expData: nil, @@ -326,14 +326,14 @@ func TestDispatchSubmessages(t *testing.T) { }, }, msgHandler: &wasmtesting.MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { events = []sdk.Event{ sdk.NewEvent("message", sdk.NewAttribute("_contract_address", contractAddr.String())), // we don't know what the contarctAddr will be so we can't use it in the final tests sdk.NewEvent("execute", sdk.NewAttribute("_contract_address", "placeholder-random-addr")), sdk.NewEvent("wasm", sdk.NewAttribute("random", "data")), } - return events, [][]byte{[]byte("subData")}, nil + return ctx, events, [][]byte{[]byte("subData")}, nil }, }, expData: []byte("subData"), diff --git a/x/wasm/keeper/wasmtesting/message_router.go b/x/wasm/keeper/wasmtesting/message_router.go index 712e012..a1ed2ab 100644 --- a/x/wasm/keeper/wasmtesting/message_router.go +++ b/x/wasm/keeper/wasmtesting/message_router.go @@ -1,17 +1,18 @@ package wasmtesting import ( - "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" ) +type MsgHandler = func(ctx sdk.Context, req sdk.Msg) (sdk.Context, *sdk.Result, error) + // MockMessageRouter mock for testing type MockMessageRouter struct { - HandlerFn func(msg sdk.Msg) baseapp.MsgServiceHandler + HandlerFn func(msg sdk.Msg) MsgHandler } // Handler is the entry point -func (m MockMessageRouter) Handler(msg sdk.Msg) baseapp.MsgServiceHandler { +func (m MockMessageRouter) Handler(msg sdk.Msg) MsgHandler { if m.HandlerFn == nil { panic("not expected to be called") } @@ -19,9 +20,9 @@ func (m MockMessageRouter) Handler(msg sdk.Msg) baseapp.MsgServiceHandler { } // MessageRouterFunc convenient type to match the keeper.MessageRouter interface -type MessageRouterFunc func(msg sdk.Msg) baseapp.MsgServiceHandler +type MessageRouterFunc func(msg sdk.Msg) MsgHandler // Handler is the entry point -func (m MessageRouterFunc) Handler(msg sdk.Msg) baseapp.MsgServiceHandler { +func (m MessageRouterFunc) Handler(msg sdk.Msg) MsgHandler { return m(msg) } diff --git a/x/wasm/keeper/wasmtesting/messenger.go b/x/wasm/keeper/wasmtesting/messenger.go index f0ef2d8..1a9745e 100644 --- a/x/wasm/keeper/wasmtesting/messenger.go +++ b/x/wasm/keeper/wasmtesting/messenger.go @@ -9,10 +9,10 @@ import ( ) type MockMessageHandler struct { - DispatchMsgFn func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (events []sdk.Event, data [][]byte, err error) + DispatchMsgFn func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) } -func (m *MockMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { +func (m *MockMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, codeInfo types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { if m.DispatchMsgFn == nil { panic("not expected to be called") } @@ -22,18 +22,18 @@ func (m *MockMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAd func NewCapturingMessageHandler() (*MockMessageHandler, *[]wasmvmtypes.CosmosMsg) { var messages []wasmvmtypes.CosmosMsg return &MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { messages = append(messages, msg) // return one data item so that this doesn't cause an error in submessage processing (it takes the first element from data) - return nil, [][]byte{{1}}, nil + return ctx, nil, [][]byte{{1}}, nil }, }, &messages } func NewErroringMessageHandler() *MockMessageHandler { return &MockMessageHandler{ - DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (events []sdk.Event, data [][]byte, err error) { - return nil, nil, errors.New("test, ignore") + DispatchMsgFn: func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg, info wasmvmtypes.MessageInfo, _ types.CodeInfo) (resCtx sdk.Context, events []sdk.Event, data [][]byte, err error) { + return ctx, nil, nil, errors.New("test, ignore") }, } } From 7b9d051619265af8df7e3f4388e79aa4b62f49d5 Mon Sep 17 00:00:00 2001 From: codchen Date: Thu, 14 Nov 2024 15:14:47 +0800 Subject: [PATCH 2/6] Emit owner event before CW721 transfer --- x/wasm/keeper/keeper.go | 49 +++++++++++++++++++++++++++++++++++++++++ x/wasm/types/events.go | 21 ++++++++++-------- 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/x/wasm/keeper/keeper.go b/x/wasm/keeper/keeper.go index 47d94b4..e4f3c57 100644 --- a/x/wasm/keeper/keeper.go +++ b/x/wasm/keeper/keeper.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/binary" + "encoding/json" "fmt" "math" "path/filepath" @@ -371,6 +372,7 @@ func (k Keeper) execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller // prepare querier querier := k.newQueryHandler(ctx, contractAddress) gas := k.runtimeGasForContract(ctx) + k.emitCW721OwnerBeforeTransferIfApplicable(ctx, contractAddress, msg) res, gasUsed, execErr := k.wasmVM.Execute(codeInfo.CodeHash, env, info, msg, prefixStore, cosmwasmAPI, querier, k.gasMeter(ctx), gas, costJSONDeserialization) k.consumeRuntimeGas(ctx, gasUsed) if execErr != nil { @@ -1072,6 +1074,53 @@ func (k Keeper) newQueryHandler(ctx sdk.Context, contractAddress sdk.AccAddress) return NewQueryHandler(ctx, k.wasmVMQueryHandler, contractAddress, k.gasRegister) } +func (k Keeper) emitCW721OwnerBeforeTransferIfApplicable(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) { + parsedMsg := map[string]json.RawMessage{} + if err := json.Unmarshal(msg, &parsedMsg); err != nil { + return + } + subMsg, ok := parsedMsg["transfer_nft"] + if !ok { + subMsg, ok = parsedMsg["send_nft"] + } + if !ok { + subMsg, ok = parsedMsg["burn"] + } + if !ok { + return + } + parsedSubMsg := map[string]json.RawMessage{} + if err := json.Unmarshal(subMsg, &parsedSubMsg); err != nil { + return + } + if tokenBz, ok := parsedSubMsg["token_id"]; ok && len(tokenBz) > 2 { + tokenID := string(tokenBz[1 : len(tokenBz)-1]) + ownerQuery := map[string]interface{}{"owner_of": map[string]string{ + "token_id": tokenID, + }} + ownerQueryBz, err := json.Marshal(ownerQuery) + if err != nil { + return + } + resBz, err := k.QuerySmart(ctx.WithGasMeter(sdk.NewInfiniteGasMeterWithMultiplier(ctx)), contractAddress, ownerQueryBz) + if err != nil { + return + } + res := map[string]json.RawMessage{} + if err := json.Unmarshal(resBz, &res); err != nil { + return + } + if ownerBz, ok := res["owner"]; ok && len(ownerBz) > 2 { + ctx.EventManager().EmitEvent(sdk.NewEvent( + types.EventTypeCW721PreTransferOwner, + sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()), + sdk.NewAttribute(types.AttributeKeyTokenId, tokenID), + sdk.NewAttribute(types.AttributeKeyOwner, string(ownerBz[1:len(ownerBz)-1])), + )) + } + } +} + // MultipliedGasMeter wraps the GasMeter from context and multiplies all reads by out defined multiplier type MultipliedGasMeter struct { originalMeter sdk.GasMeter diff --git a/x/wasm/types/events.go b/x/wasm/types/events.go index 0c32476..2bdd6a2 100644 --- a/x/wasm/types/events.go +++ b/x/wasm/types/events.go @@ -6,15 +6,16 @@ const ( // CustomContractEventPrefix contracts can create custom events. To not mix them with other system events they got the `wasm-` prefix. CustomContractEventPrefix = "wasm-" - EventTypeStoreCode = "store_code" - EventTypeInstantiate = "instantiate" - EventTypeExecute = "execute" - EventTypeMigrate = "migrate" - EventTypePinCode = "pin_code" - EventTypeUnpinCode = "unpin_code" - EventTypeSudo = "sudo" - EventTypeReply = "reply" - EventTypeGovContractResult = "gov_contract_result" + EventTypeStoreCode = "store_code" + EventTypeInstantiate = "instantiate" + EventTypeExecute = "execute" + EventTypeMigrate = "migrate" + EventTypePinCode = "pin_code" + EventTypeUnpinCode = "unpin_code" + EventTypeSudo = "sudo" + EventTypeReply = "reply" + EventTypeGovContractResult = "gov_contract_result" + EventTypeCW721PreTransferOwner = "cw721_pretransfer_owner" ) // event attributes returned from contract execution @@ -25,4 +26,6 @@ const ( AttributeKeyCodeID = "code_id" AttributeKeyResultDataHex = "result" AttributeKeyFeature = "feature" + AttributeKeyTokenId = "token_id" + AttributeKeyOwner = "owner" ) From 033c3dde6706e3216b574febc3ed29bab87065ab Mon Sep 17 00:00:00 2001 From: Philip Su Date: Thu, 14 Nov 2024 08:46:56 -0800 Subject: [PATCH 3/6] Set owner for mint use case --- x/wasm/keeper/keeper.go | 83 +++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/x/wasm/keeper/keeper.go b/x/wasm/keeper/keeper.go index e4f3c57..8dea64c 100644 --- a/x/wasm/keeper/keeper.go +++ b/x/wasm/keeper/keeper.go @@ -1079,45 +1079,64 @@ func (k Keeper) emitCW721OwnerBeforeTransferIfApplicable(ctx sdk.Context, contra if err := json.Unmarshal(msg, &parsedMsg); err != nil { return } - subMsg, ok := parsedMsg["transfer_nft"] - if !ok { - subMsg, ok = parsedMsg["send_nft"] - } - if !ok { - subMsg, ok = parsedMsg["burn"] - } - if !ok { - return - } - parsedSubMsg := map[string]json.RawMessage{} - if err := json.Unmarshal(subMsg, &parsedSubMsg); err != nil { - return - } - if tokenBz, ok := parsedSubMsg["token_id"]; ok && len(tokenBz) > 2 { - tokenID := string(tokenBz[1 : len(tokenBz)-1]) - ownerQuery := map[string]interface{}{"owner_of": map[string]string{ - "token_id": tokenID, - }} - ownerQueryBz, err := json.Marshal(ownerQuery) - if err != nil { + // For mint cases, we directly set owner to be 0x0 + subMsg, ok := parsedMsg["mint"] + if ok { + parsedSubMsg := map[string]json.RawMessage{} + if err := json.Unmarshal(subMsg, &parsedSubMsg); err != nil { return } - resBz, err := k.QuerySmart(ctx.WithGasMeter(sdk.NewInfiniteGasMeterWithMultiplier(ctx)), contractAddress, ownerQueryBz) - if err != nil { - return - } - res := map[string]json.RawMessage{} - if err := json.Unmarshal(resBz, &res); err != nil { - return - } - if ownerBz, ok := res["owner"]; ok && len(ownerBz) > 2 { + if tokenBz, ok := parsedSubMsg["token_id"]; ok && len(tokenBz) > 2 { + tokenID := string(tokenBz[1 : len(tokenBz)-1]) ctx.EventManager().EmitEvent(sdk.NewEvent( types.EventTypeCW721PreTransferOwner, sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()), sdk.NewAttribute(types.AttributeKeyTokenId, tokenID), - sdk.NewAttribute(types.AttributeKeyOwner, string(ownerBz[1:len(ownerBz)-1])), - )) + sdk.NewAttribute(types.AttributeKeyOwner, string("0x0000000000000000000000000000000000000000"), + ))) + } else { + subMsg, ok := parsedMsg["transfer_nft"] + if !ok { + subMsg, ok = parsedMsg["send_nft"] + } + if !ok { + subMsg, ok = parsedMsg["burn"] + } + if !ok { + return + } + parsedSubMsg := map[string]json.RawMessage{} + if err := json.Unmarshal(subMsg, &parsedSubMsg); err != nil { + return + } + if tokenBz, ok := parsedSubMsg["token_id"]; ok && len(tokenBz) > 2 { + tokenID := string(tokenBz[1 : len(tokenBz)-1]) + ownerQuery := map[string]interface{}{"owner_of": map[string]string{ + "token_id": tokenID, + }} + ownerQueryBz, err := json.Marshal(ownerQuery) + if err != nil { + return + } + resBz, err := k.QuerySmart(ctx.WithGasMeter(sdk.NewInfiniteGasMeterWithMultiplier(ctx)), contractAddress, ownerQueryBz) + if err != nil { + return + } + res := map[string]json.RawMessage{} + if err := json.Unmarshal(resBz, &res); err != nil { + return + } + if ownerBz, ok := res["owner"]; ok && len(ownerBz) > 2 { + ctx.EventManager().EmitEvent(sdk.NewEvent( + types.EventTypeCW721PreTransferOwner, + sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()), + sdk.NewAttribute(types.AttributeKeyTokenId, tokenID), + sdk.NewAttribute(types.AttributeKeyOwner, string(ownerBz[1:len(ownerBz)-1])), + )) + } + } } + } } From dceb696056942f9e8d6375e3ada2cb91e62440cd Mon Sep 17 00:00:00 2001 From: Philip Su Date: Thu, 14 Nov 2024 09:03:05 -0800 Subject: [PATCH 4/6] fmt --- x/wasm/keeper/keeper.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x/wasm/keeper/keeper.go b/x/wasm/keeper/keeper.go index 8dea64c..f88983e 100644 --- a/x/wasm/keeper/keeper.go +++ b/x/wasm/keeper/keeper.go @@ -1092,8 +1092,7 @@ func (k Keeper) emitCW721OwnerBeforeTransferIfApplicable(ctx sdk.Context, contra types.EventTypeCW721PreTransferOwner, sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()), sdk.NewAttribute(types.AttributeKeyTokenId, tokenID), - sdk.NewAttribute(types.AttributeKeyOwner, string("0x0000000000000000000000000000000000000000"), - ))) + sdk.NewAttribute(types.AttributeKeyOwner, string("0x0000000000000000000000000000000000000000")))) } else { subMsg, ok := parsedMsg["transfer_nft"] if !ok { From 39870c23a38a8bb91d22b41435c66dd572277428 Mon Sep 17 00:00:00 2001 From: Philip Su Date: Thu, 14 Nov 2024 10:41:12 -0800 Subject: [PATCH 5/6] fix 0x0 addr --- x/wasm/keeper/keeper.go | 74 ++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/x/wasm/keeper/keeper.go b/x/wasm/keeper/keeper.go index f88983e..5611a54 100644 --- a/x/wasm/keeper/keeper.go +++ b/x/wasm/keeper/keeper.go @@ -1092,51 +1092,51 @@ func (k Keeper) emitCW721OwnerBeforeTransferIfApplicable(ctx sdk.Context, contra types.EventTypeCW721PreTransferOwner, sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()), sdk.NewAttribute(types.AttributeKeyTokenId, tokenID), - sdk.NewAttribute(types.AttributeKeyOwner, string("0x0000000000000000000000000000000000000000")))) - } else { - subMsg, ok := parsedMsg["transfer_nft"] - if !ok { - subMsg, ok = parsedMsg["send_nft"] - } - if !ok { - subMsg, ok = parsedMsg["burn"] + sdk.NewAttribute(types.AttributeKeyOwner, ""))) + } + } else { + subMsg, ok := parsedMsg["transfer_nft"] + if !ok { + subMsg, ok = parsedMsg["send_nft"] + } + if !ok { + subMsg, ok = parsedMsg["burn"] + } + if !ok { + return + } + parsedSubMsg := map[string]json.RawMessage{} + if err := json.Unmarshal(subMsg, &parsedSubMsg); err != nil { + return + } + if tokenBz, ok := parsedSubMsg["token_id"]; ok && len(tokenBz) > 2 { + tokenID := string(tokenBz[1 : len(tokenBz)-1]) + ownerQuery := map[string]interface{}{"owner_of": map[string]string{ + "token_id": tokenID, + }} + ownerQueryBz, err := json.Marshal(ownerQuery) + if err != nil { + return } - if !ok { + resBz, err := k.QuerySmart(ctx.WithGasMeter(sdk.NewInfiniteGasMeterWithMultiplier(ctx)), contractAddress, ownerQueryBz) + if err != nil { return } - parsedSubMsg := map[string]json.RawMessage{} - if err := json.Unmarshal(subMsg, &parsedSubMsg); err != nil { + res := map[string]json.RawMessage{} + if err := json.Unmarshal(resBz, &res); err != nil { return } - if tokenBz, ok := parsedSubMsg["token_id"]; ok && len(tokenBz) > 2 { - tokenID := string(tokenBz[1 : len(tokenBz)-1]) - ownerQuery := map[string]interface{}{"owner_of": map[string]string{ - "token_id": tokenID, - }} - ownerQueryBz, err := json.Marshal(ownerQuery) - if err != nil { - return - } - resBz, err := k.QuerySmart(ctx.WithGasMeter(sdk.NewInfiniteGasMeterWithMultiplier(ctx)), contractAddress, ownerQueryBz) - if err != nil { - return - } - res := map[string]json.RawMessage{} - if err := json.Unmarshal(resBz, &res); err != nil { - return - } - if ownerBz, ok := res["owner"]; ok && len(ownerBz) > 2 { - ctx.EventManager().EmitEvent(sdk.NewEvent( - types.EventTypeCW721PreTransferOwner, - sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()), - sdk.NewAttribute(types.AttributeKeyTokenId, tokenID), - sdk.NewAttribute(types.AttributeKeyOwner, string(ownerBz[1:len(ownerBz)-1])), - )) - } + if ownerBz, ok := res["owner"]; ok && len(ownerBz) > 2 { + ctx.EventManager().EmitEvent(sdk.NewEvent( + types.EventTypeCW721PreTransferOwner, + sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()), + sdk.NewAttribute(types.AttributeKeyTokenId, tokenID), + sdk.NewAttribute(types.AttributeKeyOwner, string(ownerBz[1:len(ownerBz)-1])), + )) } } - } + } // MultipliedGasMeter wraps the GasMeter from context and multiplies all reads by out defined multiplier From 1e24a488b20e84024f1fa3b06eb95a7f9b328976 Mon Sep 17 00:00:00 2001 From: Philip Su Date: Thu, 14 Nov 2024 11:05:04 -0800 Subject: [PATCH 6/6] revert --- x/wasm/keeper/keeper.go | 82 ++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 50 deletions(-) diff --git a/x/wasm/keeper/keeper.go b/x/wasm/keeper/keeper.go index 5611a54..e4f3c57 100644 --- a/x/wasm/keeper/keeper.go +++ b/x/wasm/keeper/keeper.go @@ -1079,64 +1079,46 @@ func (k Keeper) emitCW721OwnerBeforeTransferIfApplicable(ctx sdk.Context, contra if err := json.Unmarshal(msg, &parsedMsg); err != nil { return } - // For mint cases, we directly set owner to be 0x0 - subMsg, ok := parsedMsg["mint"] - if ok { - parsedSubMsg := map[string]json.RawMessage{} - if err := json.Unmarshal(subMsg, &parsedSubMsg); err != nil { + subMsg, ok := parsedMsg["transfer_nft"] + if !ok { + subMsg, ok = parsedMsg["send_nft"] + } + if !ok { + subMsg, ok = parsedMsg["burn"] + } + if !ok { + return + } + parsedSubMsg := map[string]json.RawMessage{} + if err := json.Unmarshal(subMsg, &parsedSubMsg); err != nil { + return + } + if tokenBz, ok := parsedSubMsg["token_id"]; ok && len(tokenBz) > 2 { + tokenID := string(tokenBz[1 : len(tokenBz)-1]) + ownerQuery := map[string]interface{}{"owner_of": map[string]string{ + "token_id": tokenID, + }} + ownerQueryBz, err := json.Marshal(ownerQuery) + if err != nil { return } - if tokenBz, ok := parsedSubMsg["token_id"]; ok && len(tokenBz) > 2 { - tokenID := string(tokenBz[1 : len(tokenBz)-1]) - ctx.EventManager().EmitEvent(sdk.NewEvent( - types.EventTypeCW721PreTransferOwner, - sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()), - sdk.NewAttribute(types.AttributeKeyTokenId, tokenID), - sdk.NewAttribute(types.AttributeKeyOwner, ""))) - } - } else { - subMsg, ok := parsedMsg["transfer_nft"] - if !ok { - subMsg, ok = parsedMsg["send_nft"] - } - if !ok { - subMsg, ok = parsedMsg["burn"] - } - if !ok { + resBz, err := k.QuerySmart(ctx.WithGasMeter(sdk.NewInfiniteGasMeterWithMultiplier(ctx)), contractAddress, ownerQueryBz) + if err != nil { return } - parsedSubMsg := map[string]json.RawMessage{} - if err := json.Unmarshal(subMsg, &parsedSubMsg); err != nil { + res := map[string]json.RawMessage{} + if err := json.Unmarshal(resBz, &res); err != nil { return } - if tokenBz, ok := parsedSubMsg["token_id"]; ok && len(tokenBz) > 2 { - tokenID := string(tokenBz[1 : len(tokenBz)-1]) - ownerQuery := map[string]interface{}{"owner_of": map[string]string{ - "token_id": tokenID, - }} - ownerQueryBz, err := json.Marshal(ownerQuery) - if err != nil { - return - } - resBz, err := k.QuerySmart(ctx.WithGasMeter(sdk.NewInfiniteGasMeterWithMultiplier(ctx)), contractAddress, ownerQueryBz) - if err != nil { - return - } - res := map[string]json.RawMessage{} - if err := json.Unmarshal(resBz, &res); err != nil { - return - } - if ownerBz, ok := res["owner"]; ok && len(ownerBz) > 2 { - ctx.EventManager().EmitEvent(sdk.NewEvent( - types.EventTypeCW721PreTransferOwner, - sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()), - sdk.NewAttribute(types.AttributeKeyTokenId, tokenID), - sdk.NewAttribute(types.AttributeKeyOwner, string(ownerBz[1:len(ownerBz)-1])), - )) - } + if ownerBz, ok := res["owner"]; ok && len(ownerBz) > 2 { + ctx.EventManager().EmitEvent(sdk.NewEvent( + types.EventTypeCW721PreTransferOwner, + sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()), + sdk.NewAttribute(types.AttributeKeyTokenId, tokenID), + sdk.NewAttribute(types.AttributeKeyOwner, string(ownerBz[1:len(ownerBz)-1])), + )) } } - } // MultipliedGasMeter wraps the GasMeter from context and multiplies all reads by out defined multiplier