github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/ibc-go/modules/core/04-channel/keeper/timeout.go (about)

     1  package keeper
     2  
     3  import (
     4  	"bytes"
     5  
     6  	types2 "github.com/fibonacci-chain/fbc/libs/tendermint/types"
     7  
     8  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
     9  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    10  	capabilitytypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/capability/types"
    11  	connectiontypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/03-connection/types"
    12  	"github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/04-channel/types"
    13  	host "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/24-host"
    14  	"github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/exported"
    15  )
    16  
    17  // TimeoutPacket is called by a module which originally attempted to send a
    18  // packet to a counterparty module, where the timeout height has passed on the
    19  // counterparty chain without the packet being committed, to prove that the
    20  // packet can no longer be executed and to allow the calling module to safely
    21  // perform appropriate state transitions. Its intended usage is within the
    22  // ante handler.
    23  func (k Keeper) TimeoutPacket(
    24  	ctx sdk.Context,
    25  	packet exported.PacketI,
    26  	proof []byte,
    27  	proofHeight exported.Height,
    28  	nextSequenceRecv uint64,
    29  ) error {
    30  	channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel())
    31  	if !found {
    32  		return sdkerrors.Wrapf(
    33  			types.ErrChannelNotFound,
    34  			"port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel(),
    35  		)
    36  	}
    37  
    38  	// NOTE: TimeoutPacket is called by the AnteHandler which acts upon the packet.Route(),
    39  	// so the capability authentication can be omitted here
    40  
    41  	if packet.GetDestPort() != channel.Counterparty.PortId {
    42  		return sdkerrors.Wrapf(
    43  			types.ErrInvalidPacket,
    44  			"packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortId,
    45  		)
    46  	}
    47  
    48  	if packet.GetDestChannel() != channel.Counterparty.ChannelId {
    49  		return sdkerrors.Wrapf(
    50  			types.ErrInvalidPacket,
    51  			"packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelId,
    52  		)
    53  	}
    54  
    55  	connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0])
    56  	if !found {
    57  		return sdkerrors.Wrap(
    58  			connectiontypes.ErrConnectionNotFound,
    59  			channel.ConnectionHops[0],
    60  		)
    61  	}
    62  
    63  	// check that timeout height or timeout timestamp has passed on the other end
    64  	proofTimestamp, err := k.connectionKeeper.GetTimestampAtHeight(ctx, connectionEnd, proofHeight)
    65  	if err != nil {
    66  		return err
    67  	}
    68  
    69  	timeoutHeight := packet.GetTimeoutHeight()
    70  	if (timeoutHeight.IsZero() || proofHeight.LT(timeoutHeight)) &&
    71  		(packet.GetTimeoutTimestamp() == 0 || proofTimestamp < packet.GetTimeoutTimestamp()) {
    72  		return sdkerrors.Wrap(types.ErrPacketTimeout, "packet timeout has not been reached for height or timestamp")
    73  	}
    74  
    75  	commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
    76  
    77  	if len(commitment) == 0 {
    78  		EmitTimeoutPacketEvent(ctx, packet, channel)
    79  		// This error indicates that the timeout has already been relayed
    80  		// or there is a misconfigured relayer attempting to prove a timeout
    81  		// for a packet never sent. Core IBC will treat this error as a no-op in order to
    82  		// prevent an entire relay transaction from failing and consuming unnecessary fees.
    83  		return types.ErrNoOpMsg
    84  	}
    85  
    86  	if channel.State != types.OPEN {
    87  		return sdkerrors.Wrapf(
    88  			types.ErrInvalidChannelState,
    89  			"channel state is not OPEN (got %s)", channel.State.String(),
    90  		)
    91  	}
    92  
    93  	packetCommitment := types.CommitPacket(k.cdc, packet)
    94  
    95  	// verify we sent the packet and haven't cleared it out yet
    96  	if !bytes.Equal(commitment, packetCommitment) {
    97  		return sdkerrors.Wrapf(types.ErrInvalidPacket, "packet commitment bytes are not equal: got (%v), expected (%v)", commitment, packetCommitment)
    98  	}
    99  
   100  	switch channel.Ordering {
   101  	case types.ORDERED:
   102  		// check that packet has not been received
   103  		if nextSequenceRecv > packet.GetSequence() {
   104  			return sdkerrors.Wrapf(
   105  				types.ErrPacketReceived,
   106  				"packet already received, next sequence receive > packet sequence (%d > %d)", nextSequenceRecv, packet.GetSequence(),
   107  			)
   108  		}
   109  
   110  		// check that the recv sequence is as claimed
   111  		err = k.connectionKeeper.VerifyNextSequenceRecv(
   112  			ctx, connectionEnd, proofHeight, proof,
   113  			packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv,
   114  		)
   115  	case types.UNORDERED:
   116  		err = k.connectionKeeper.VerifyPacketReceiptAbsence(
   117  			ctx, connectionEnd, proofHeight, proof,
   118  			packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(),
   119  		)
   120  	default:
   121  		panic(sdkerrors.Wrapf(types.ErrInvalidChannelOrdering, channel.Ordering.String()))
   122  	}
   123  
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	// NOTE: the remaining code is located in the TimeoutExecuted function
   129  	return nil
   130  }
   131  
   132  // TimeoutExecuted deletes the commitment send from this chain after it verifies timeout.
   133  // If the timed-out packet came from an ORDERED channel then this channel will be closed.
   134  //
   135  // CONTRACT: this function must be called in the IBC handler
   136  func (k Keeper) TimeoutExecuted(
   137  	ctx sdk.Context,
   138  	chanCap *capabilitytypes.Capability,
   139  	packet exported.PacketI,
   140  ) error {
   141  	channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel())
   142  	if !found {
   143  		return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel())
   144  	}
   145  
   146  	capName := host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel())
   147  	if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) {
   148  		return sdkerrors.Wrapf(
   149  			types.ErrChannelCapabilityNotFound,
   150  			"caller does not own capability for channel with capability name %s", capName,
   151  		)
   152  	}
   153  
   154  	k.deletePacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
   155  
   156  	if channel.Ordering == types.ORDERED {
   157  		channel.State = types.CLOSED
   158  		k.SetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), channel)
   159  	}
   160  
   161  	k.Logger(ctx).Info(
   162  		"packet timed-out",
   163  		"sequence", packet.GetSequence(),
   164  		"src_port", packet.GetSourcePort(),
   165  		"src_channel", packet.GetSourceChannel(),
   166  		"dst_port", packet.GetDestPort(),
   167  		"dst_channel", packet.GetDestChannel(),
   168  	)
   169  
   170  	// emit an event marking that we have processed the timeout
   171  	EmitTimeoutPacketEvent(ctx, packet, channel)
   172  
   173  	if channel.Ordering == types.ORDERED && channel.State == types.CLOSED {
   174  		EmitChannelClosedEvent(ctx, packet, channel)
   175  	}
   176  
   177  	return nil
   178  }
   179  
   180  // TimeoutOnClose is called by a module in order to prove that the channel to
   181  // which an unreceived packet was addressed has been closed, so the packet will
   182  // never be received (even if the timeoutHeight has not yet been reached).
   183  func (k Keeper) TimeoutOnClose(
   184  	ctx sdk.Context,
   185  	chanCap *capabilitytypes.Capability,
   186  	packet exported.PacketI,
   187  	proof,
   188  	proofClosed []byte,
   189  	proofHeight exported.Height,
   190  	nextSequenceRecv uint64,
   191  ) error {
   192  	channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel())
   193  	if !found {
   194  		return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel())
   195  	}
   196  
   197  	capName := host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel())
   198  	if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) {
   199  		return sdkerrors.Wrapf(
   200  			types.ErrInvalidChannelCapability,
   201  			"channel capability failed authentication with capability name %s", capName,
   202  		)
   203  	}
   204  
   205  	if packet.GetDestPort() != channel.Counterparty.PortId {
   206  		return sdkerrors.Wrapf(
   207  			types.ErrInvalidPacket,
   208  			"packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortId,
   209  		)
   210  	}
   211  
   212  	if packet.GetDestChannel() != channel.Counterparty.ChannelId {
   213  		return sdkerrors.Wrapf(
   214  			types.ErrInvalidPacket,
   215  			"packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelId,
   216  		)
   217  	}
   218  
   219  	connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0])
   220  	if !found {
   221  		return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0])
   222  	}
   223  
   224  	commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
   225  
   226  	if len(commitment) == 0 {
   227  		EmitTimeoutPacketEvent(ctx, packet, channel)
   228  		// This error indicates that the timeout has already been relayed
   229  		// or there is a misconfigured relayer attempting to prove a timeout
   230  		// for a packet never sent. Core IBC will treat this error as a no-op in order to
   231  		// prevent an entire relay transaction from failing and consuming unnecessary fees.
   232  		return types.ErrNoOpMsg
   233  	}
   234  
   235  	packetCommitment := types.CommitPacket(k.cdc, packet)
   236  
   237  	// verify we sent the packet and haven't cleared it out yet
   238  	if !bytes.Equal(commitment, packetCommitment) {
   239  		return sdkerrors.Wrapf(types.ErrInvalidPacket, "packet commitment bytes are not equal: got (%v), expected (%v)", commitment, packetCommitment)
   240  	}
   241  
   242  	var counterpartyHops []string
   243  	if types2.HigherThanVenus4(ctx.BlockHeight()) {
   244  		counterpartyHops = []string{connectionEnd.GetCounterparty().GetConnectionID()}
   245  	} else {
   246  		counterpartyHops, found = k.CounterpartyHops(ctx, channel)
   247  		if !found {
   248  			// Should not reach here, connectionEnd was able to be retrieved above
   249  			panic("cannot find connection")
   250  		}
   251  	}
   252  
   253  	counterparty := types.NewCounterparty(packet.GetSourcePort(), packet.GetSourceChannel())
   254  	expectedChannel := types.NewChannel(
   255  		types.CLOSED, channel.Ordering, counterparty, counterpartyHops, channel.Version,
   256  	)
   257  
   258  	// check that the opposing channel end has closed
   259  	if err := k.connectionKeeper.VerifyChannelState(
   260  		ctx, connectionEnd, proofHeight, proofClosed,
   261  		channel.Counterparty.PortId, channel.Counterparty.ChannelId,
   262  		expectedChannel,
   263  	); err != nil {
   264  		return err
   265  	}
   266  
   267  	var err error
   268  	switch channel.Ordering {
   269  	case types.ORDERED:
   270  		// check that packet has not been received
   271  		if nextSequenceRecv > packet.GetSequence() {
   272  			return sdkerrors.Wrapf(types.ErrInvalidPacket, "packet already received, next sequence receive > packet sequence (%d > %d", nextSequenceRecv, packet.GetSequence())
   273  		}
   274  
   275  		// check that the recv sequence is as claimed
   276  		err = k.connectionKeeper.VerifyNextSequenceRecv(
   277  			ctx, connectionEnd, proofHeight, proof,
   278  			packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv,
   279  		)
   280  	case types.UNORDERED:
   281  		err = k.connectionKeeper.VerifyPacketReceiptAbsence(
   282  			ctx, connectionEnd, proofHeight, proof,
   283  			packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(),
   284  		)
   285  	default:
   286  		panic(sdkerrors.Wrapf(types.ErrInvalidChannelOrdering, channel.Ordering.String()))
   287  	}
   288  
   289  	if err != nil {
   290  		return err
   291  	}
   292  
   293  	// NOTE: the remaining code is located in the TimeoutExecuted function
   294  	return nil
   295  }