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  }