github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/ibc-go/modules/apps/27-interchain-accounts/host/keeper/relay.go (about) 1 package keeper 2 3 import ( 4 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 5 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 6 txmsg "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/ibc-adapter" 7 ibc_tx "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/ibc-tx" 8 "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/apps/27-interchain-accounts/host/types" 9 icatypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/apps/27-interchain-accounts/types" 10 channeltypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/04-channel/types" 11 "github.com/gogo/protobuf/proto" 12 ) 13 14 // OnRecvPacket handles a given interchain accounts packet on a destination host chain. 15 // If the transaction is successfully executed, the transaction response bytes will be returned. 16 func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet) ([]byte, error) { 17 var data icatypes.InterchainAccountPacketData 18 19 if err := icatypes.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil { 20 // UnmarshalJSON errors are indeterminate and therefore are not wrapped and included in failed acks 21 return nil, sdkerrors.Wrapf(icatypes.ErrUnknownDataType, "cannot unmarshal ICS-27 interchain account packet data") 22 } 23 24 switch data.Type { 25 case icatypes.EXECUTE_TX: 26 msgs, err := icatypes.DeserializeCosmosTx(k.cdc, data.Data) 27 if err != nil { 28 return nil, err 29 } 30 31 txResponse, err := k.executeTx(ctx, packet.SourcePort, packet.DestinationPort, packet.DestinationChannel, msgs) 32 if err != nil { 33 return nil, err 34 } 35 36 return txResponse, nil 37 default: 38 return nil, icatypes.ErrUnknownDataType 39 } 40 } 41 42 // executeTx attempts to execute the provided transaction. It begins by authenticating the transaction signer. 43 // If authentication succeeds, it does basic validation of the messages before attempting to deliver each message 44 // into state. The state changes will only be committed if all messages in the transaction succeed. Thus the 45 // execution of the transaction is atomic, all state changes are reverted if a single message fails. 46 func (k Keeper) executeTx(ctx sdk.Context, sourcePort, destPort, destChannel string, msgs []sdk.MsgAdapter) ([]byte, error) { 47 channel, found := k.channelKeeper.GetChannel(ctx, destPort, destChannel) 48 if !found { 49 return nil, channeltypes.ErrChannelNotFound 50 } 51 52 if err := k.authenticateTx(ctx, msgs, channel.ConnectionHops[0], sourcePort); err != nil { 53 return nil, err 54 } 55 56 txMsgData := &txmsg.TxMsgData{ 57 Data: make([]*txmsg.MsgData, len(msgs)), 58 } 59 60 // CacheContext returns a new context with the multi-store branched into a cached storage object 61 // writeCache is called only if all msgs succeed, performing state transitions atomically 62 cacheCtx, writeCache := ctx.CacheContext() 63 for i, msg := range msgs { 64 if err := msg.ValidateBasic(); err != nil { 65 return nil, err 66 } 67 68 msgResponse, err := k.executeMsg(cacheCtx, msg) 69 if err != nil { 70 return nil, err 71 } 72 73 txMsgData.Data[i] = &txmsg.MsgData{ 74 MsgType: sdk.MsgTypeURL(msg), 75 Data: msgResponse, 76 } 77 78 } 79 80 // NOTE: The context returned by CacheContext() creates a new EventManager, so events must be correctly propagated back to the current context 81 ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) 82 writeCache() 83 84 txResponse, err := proto.Marshal(txMsgData) 85 if err != nil { 86 return nil, sdkerrors.Wrap(err, "failed to marshal tx data") 87 } 88 89 return txResponse, nil 90 } 91 92 // authenticateTx ensures the provided msgs contain the correct interchain account signer address retrieved 93 // from state using the provided controller port identifier 94 func (k Keeper) authenticateTx(ctx sdk.Context, msgs []sdk.MsgAdapter, connectionID, portID string) error { 95 interchainAccountAddr, found := k.GetInterchainAccountAddress(ctx, connectionID, portID) 96 if !found { 97 return sdkerrors.Wrapf(icatypes.ErrInterchainAccountNotFound, "failed to retrieve interchain account on port %s", portID) 98 } 99 100 allowMsgs := k.GetAllowMessages(ctx) 101 for _, msg := range msgs { 102 if !types.ContainsMsgType(allowMsgs, msg) { 103 return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "message type not allowed: %s", sdk.MsgTypeURL(msg)) 104 } 105 106 for _, signer := range msg.GetSigners() { 107 if interchainAccountAddr != signer.String() { 108 return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "unexpected signer address: expected %s, got %s", interchainAccountAddr, signer.String()) 109 } 110 } 111 } 112 113 return nil 114 } 115 116 // Attempts to get the message handler from the router and if found will then execute the message. 117 // If the message execution is successful, the proto marshaled message response will be returned. 118 func (k Keeper) executeMsg(ctx sdk.Context, msg sdk.MsgAdapter) ([]byte, error) { 119 handler := k.msgRouter.HandlerWithMsg(msg) 120 if handler == nil { 121 return nil, icatypes.ErrInvalidRoute 122 } 123 124 if sen, ok := msg.(ibc_tx.MessageSensitive); ok { 125 if swapMsg, err := sen.Swap(ctx); nil != err { 126 return nil, err 127 } else if swapMsg != nil { 128 msg = swapMsg.(sdk.MsgAdapter) 129 } 130 } 131 132 res, err := handler(ctx, msg) 133 if err != nil { 134 return nil, err 135 } 136 137 // NOTE: The sdk msg handler creates e new EventManager, so events must be correctly propagated back to the current context 138 ctx.EventManager().EmitEvents(res.Events) 139 140 return res.Data, nil 141 }