From 705805c3818ab47206ce802086b1f09be751d764 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 31 Jan 2024 12:14:24 +0100 Subject: [PATCH] Add flattened msgResponses to SubMsgResponse --- x/wasm/keeper/handler_plugin.go | 56 ++++++++++++++++++--------------- x/wasm/keeper/msg_dispatcher.go | 29 ++++++++++++----- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/x/wasm/keeper/handler_plugin.go b/x/wasm/keeper/handler_plugin.go index 74dea048a7..e472c95dda 100644 --- a/x/wasm/keeper/handler_plugin.go +++ b/x/wasm/keeper/handler_plugin.go @@ -12,6 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -66,18 +67,19 @@ func NewSDKMessageHandler(cdc codec.Codec, router MessageRouter, encoders msgEnc } } -func (h SDKMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) { +func (h SDKMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, msgResponses [][]*codectypes.Any, err error) { sdkMsgs, err := h.encoders.Encode(ctx, contractAddr, contractIBCPortID, msg) if err != nil { - return nil, nil, err + return nil, nil, nil, err } for _, sdkMsg := range sdkMsgs { res, err := h.handleSdkMessage(ctx, contractAddr, sdkMsg) if err != nil { - return nil, nil, err + return nil, nil, nil, err } - // append data + // append data and msgResponses data = append(data, res.Data) + msgResponses = append(msgResponses, res.MsgResponses) // append events sdkEvents := make([]sdk.Event, len(res.Events)) for i := range res.Events { @@ -141,19 +143,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) ([]sdk.Event, [][]byte, error) { +func (m MessageHandlerChain) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) { for _, h := range m.handlers { - events, data, err := h.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg) + events, data, msgResponses, err := h.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg) switch { case err == nil: - return events, data, nil + return events, data, msgResponses, nil case errors.Is(err, types.ErrUnknownMsg): continue default: - return events, data, err + return events, data, msgResponses, err } } - return nil, nil, errorsmod.Wrap(types.ErrUnknownMsg, "no handler found") + return nil, nil, nil, errorsmod.Wrap(types.ErrUnknownMsg, "no handler found") } // IBCRawPacketHandler handles IBC.SendPacket messages which are published to an IBC channel. @@ -173,67 +175,69 @@ func NewIBCRawPacketHandler(ics4Wrapper types.ICS4Wrapper, channelKeeper types.C } // DispatchMsg publishes a raw IBC packet onto the channel. -func (h IBCRawPacketHandler) DispatchMsg(ctx sdk.Context, _ sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) ([]sdk.Event, [][]byte, error) { +func (h IBCRawPacketHandler) DispatchMsg(ctx sdk.Context, _ sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) { if msg.IBC == nil || msg.IBC.SendPacket == nil { - return nil, nil, types.ErrUnknownMsg + return nil, nil, nil, types.ErrUnknownMsg } if contractIBCPortID == "" { - return nil, nil, errorsmod.Wrapf(types.ErrUnsupportedForContract, "ibc not supported") + return nil, nil, nil, errorsmod.Wrapf(types.ErrUnsupportedForContract, "ibc not supported") } contractIBCChannelID := msg.IBC.SendPacket.ChannelID if contractIBCChannelID == "" { - return nil, nil, errorsmod.Wrapf(types.ErrEmpty, "ibc channel") + return nil, nil, nil, errorsmod.Wrapf(types.ErrEmpty, "ibc channel") } channelCap, ok := h.capabilityKeeper.GetCapability(ctx, host.ChannelCapabilityPath(contractIBCPortID, contractIBCChannelID)) if !ok { - return nil, nil, errorsmod.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") + return nil, nil, nil, errorsmod.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") } seq, err := h.ics4Wrapper.SendPacket(ctx, channelCap, contractIBCPortID, contractIBCChannelID, ConvertWasmIBCTimeoutHeightToCosmosHeight(msg.IBC.SendPacket.Timeout.Block), msg.IBC.SendPacket.Timeout.Timestamp, msg.IBC.SendPacket.Data) if err != nil { - return nil, nil, errorsmod.Wrap(err, "channel") + return nil, nil, nil, errorsmod.Wrap(err, "channel") } moduleLogger(ctx).Debug("ibc packet set", "seq", seq) + var msgResponse [][]*codectypes.Any resp := &types.MsgIBCSendResponse{Sequence: seq} val, err := resp.Marshal() if err != nil { - return nil, nil, errorsmod.Wrap(err, "failed to marshal IBC send response") + return nil, nil, nil, errorsmod.Wrap(err, "failed to marshal IBC send response") } + // TODO: fill msgResponse - return nil, [][]byte{val}, nil + return nil, [][]byte{val}, msgResponse, nil } 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) (events []sdk.Event, data [][]byte, err error) +type MessageHandlerFunc func(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, msgResponses [][]*codectypes.Any, 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) (events []sdk.Event, data [][]byte, err error) { +func (m MessageHandlerFunc) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, msgResponses [][]*codectypes.Any, err error) { return m(ctx, contractAddr, contractIBCPortID, msg) } // NewBurnCoinMessageHandler handles wasmvm.BurnMsg messages func NewBurnCoinMessageHandler(burner types.Burner) MessageHandlerFunc { - return func(ctx sdk.Context, contractAddr sdk.AccAddress, _ string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) { + return func(ctx sdk.Context, contractAddr sdk.AccAddress, _ string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, msgResponses [][]*codectypes.Any, 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 nil, nil, nil, err } if coins.IsZero() { - return nil, nil, types.ErrEmpty.Wrap("amount") + return nil, nil, nil, types.ErrEmpty.Wrap("amount") } if err := burner.SendCoinsFromAccountToModule(ctx, contractAddr, types.ModuleName, coins); err != nil { - return nil, nil, errorsmod.Wrap(err, "transfer to module") + return nil, nil, nil, errorsmod.Wrap(err, "transfer to module") } if err := burner.BurnCoins(ctx, types.ModuleName, coins); err != nil { - return nil, nil, errorsmod.Wrap(err, "burn coins") + return nil, nil, nil, errorsmod.Wrap(err, "burn coins") } moduleLogger(ctx).Info("Burned", "amount", coins) - return nil, nil, nil + return nil, nil, nil, nil } - return nil, nil, types.ErrUnknownMsg + return nil, nil, nil, types.ErrUnknownMsg } } diff --git a/x/wasm/keeper/msg_dispatcher.go b/x/wasm/keeper/msg_dispatcher.go index cbd66eed1f..a69a3e31a5 100644 --- a/x/wasm/keeper/msg_dispatcher.go +++ b/x/wasm/keeper/msg_dispatcher.go @@ -11,6 +11,7 @@ import ( errorsmod "cosmossdk.io/errors" storetypes "cosmossdk.io/store/types" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -20,7 +21,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) (events []sdk.Event, data [][]byte, err error) + DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, msgResponses [][]*codectypes.Any, err error) } // replyer is a subset of keeper that can handle replies to submessages @@ -42,7 +43,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) error { for _, msg := range msgs { - events, _, err := d.messenger.DispatchMsg(ctx, contractAddr, ibcPort, msg) + events, _, _, err := d.messenger.DispatchMsg(ctx, contractAddr, ibcPort, msg) if err != nil { return err } @@ -53,7 +54,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) (events []sdk.Event, data [][]byte, err error) { +func (d MessageDispatcher) dispatchMsgWithGasLimit(ctx sdk.Context, contractAddr sdk.AccAddress, ibcPort string, msg wasmvmtypes.CosmosMsg, gasLimit uint64) (events []sdk.Event, data [][]byte, msgResponses [][]*codectypes.Any, err error) { limitedMeter := storetypes.NewGasMeter(gasLimit) subCtx := ctx.WithGasMeter(limitedMeter) @@ -70,13 +71,13 @@ func (d MessageDispatcher) dispatchMsgWithGasLimit(ctx sdk.Context, contractAddr err = errorsmod.Wrap(sdkerrors.ErrOutOfGas, "SubMsg hit gas limit") } }() - events, data, err = d.messenger.DispatchMsg(subCtx, contractAddr, ibcPort, msg) + events, data, msgResponses, err = d.messenger.DispatchMsg(subCtx, contractAddr, ibcPort, msg) // 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 events, data, msgResponses, err } // DispatchSubmessages builds a sandbox to execute these messages and returns the execution result to the contract @@ -101,10 +102,11 @@ func (d MessageDispatcher) DispatchSubmessages(ctx sdk.Context, contractAddr sdk var err error var events []sdk.Event var data [][]byte + var msgResponses [][]*codectypes.Any if limitGas { - events, data, err = d.dispatchMsgWithGasLimit(subCtx, contractAddr, ibcPort, msg.Msg, *msg.GasLimit) + events, data, msgResponses, err = d.dispatchMsgWithGasLimit(subCtx, contractAddr, ibcPort, msg.Msg, *msg.GasLimit) } else { - events, data, err = d.messenger.DispatchMsg(subCtx, contractAddr, ibcPort, msg.Msg) + events, data, msgResponses, err = d.messenger.DispatchMsg(subCtx, contractAddr, ibcPort, msg.Msg) } // if it succeeds, commit state changes from submessage, and pass on events to Event Manager @@ -142,10 +144,23 @@ func (d MessageDispatcher) DispatchSubmessages(ctx sdk.Context, contractAddr sdk if len(data) > 0 { responseData = data[0] } + + // For msgResponses we flatten the nested list into a flat list. In the majority of cases + // we only expect one message to be emitted and one response per message. But it might be possible + // to create multiple SDK messages from one CosmWasm message or we have multiple responses for one message. + // See https://github.com/CosmWasm/cosmwasm/issues/2009 for more information. + var msgResponsesFlattened []*codectypes.Any + for _, singleMsgResponses := range msgResponses { + msgResponsesFlattened = append(msgResponsesFlattened, singleMsgResponses...) + } + + _ = msgResponsesFlattened // silences unused lint + result = wasmvmtypes.SubMsgResult{ Ok: &wasmvmtypes.SubMsgResponse{ Events: sdkEventsToWasmVMEvents(filteredEvents), Data: responseData, + // msgResponsesFlattened goes here }, } } else {