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

     1  package keeper
     2  
     3  import (
     4  	"bytes"
     5  	"time"
     6  
     7  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
     8  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
     9  	capabilitytypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/capability/types"
    10  	clienttypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/02-client/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  // SendPacket is called by a module in order to send an IBC packet on a channel
    18  // end owned by the calling module to the corresponding module on the counterparty
    19  // chain.
    20  func (k Keeper) SendPacket(
    21  	ctx sdk.Context,
    22  	channelCap *capabilitytypes.Capability,
    23  	packet exported.PacketI,
    24  ) error {
    25  	if err := packet.ValidateBasic(); err != nil {
    26  		return sdkerrors.Wrap(err, "packet failed basic validation")
    27  	}
    28  
    29  	channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel())
    30  	if !found {
    31  		return sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetSourceChannel())
    32  	}
    33  
    34  	if channel.State == types.CLOSED {
    35  		return sdkerrors.Wrapf(
    36  			types.ErrInvalidChannelState,
    37  			"channel is CLOSED (got %s)", channel.State.String(),
    38  		)
    39  	}
    40  
    41  	if !k.scopedKeeper.AuthenticateCapability(ctx, channelCap, host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel())) {
    42  		return sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel())
    43  	}
    44  
    45  	if packet.GetDestPort() != channel.Counterparty.PortId {
    46  		return sdkerrors.Wrapf(
    47  			types.ErrInvalidPacket,
    48  			"packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortId,
    49  		)
    50  	}
    51  
    52  	if packet.GetDestChannel() != channel.Counterparty.ChannelId {
    53  		return sdkerrors.Wrapf(
    54  			types.ErrInvalidPacket,
    55  			"packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelId,
    56  		)
    57  	}
    58  
    59  	connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0])
    60  	if !found {
    61  		return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0])
    62  	}
    63  
    64  	clientState, found := k.clientKeeper.GetClientState(ctx, connectionEnd.GetClientID())
    65  	if !found {
    66  		return clienttypes.ErrConsensusStateNotFound
    67  	}
    68  
    69  	// prevent accidental sends with clients that cannot be updated
    70  	clientStore := k.clientKeeper.ClientStore(ctx, connectionEnd.GetClientID())
    71  	if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active {
    72  		return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "cannot send packet using client (%s) with status %s", connectionEnd.GetClientID(), status)
    73  	}
    74  
    75  	// check if packet is timed out on the receiving chain
    76  	latestHeight := clientState.GetLatestHeight()
    77  	timeoutHeight := packet.GetTimeoutHeight()
    78  	if !timeoutHeight.IsZero() && latestHeight.GTE(timeoutHeight) {
    79  		return sdkerrors.Wrapf(
    80  			types.ErrPacketTimeout,
    81  			"receiving chain block height >= packet timeout height (%s >= %s)", latestHeight, timeoutHeight,
    82  		)
    83  	}
    84  
    85  	clientType, _, err := clienttypes.ParseClientIdentifier(connectionEnd.GetClientID())
    86  	if err != nil {
    87  		return err
    88  	}
    89  
    90  	// NOTE: this is a temporary fix. Solo machine does not support usage of 'GetTimestampAtHeight'
    91  	// A future change should move this function to be a ClientState callback.
    92  	if clientType != exported.Solomachine {
    93  		latestTimestamp, err := k.connectionKeeper.GetTimestampAtHeight(ctx, connectionEnd, latestHeight)
    94  		if err != nil {
    95  			return err
    96  		}
    97  
    98  		if packet.GetTimeoutTimestamp() != 0 && latestTimestamp >= packet.GetTimeoutTimestamp() {
    99  			return sdkerrors.Wrapf(
   100  				types.ErrPacketTimeout,
   101  				"receiving chain block timestamp >= packet timeout timestamp (%s >= %s)", time.Unix(0, int64(latestTimestamp)), time.Unix(0, int64(packet.GetTimeoutTimestamp())),
   102  			)
   103  		}
   104  	}
   105  
   106  	nextSequenceSend, found := k.GetNextSequenceSend(ctx, packet.GetSourcePort(), packet.GetSourceChannel())
   107  	if !found {
   108  		return sdkerrors.Wrapf(
   109  			types.ErrSequenceSendNotFound,
   110  			"source port: %s, source channel: %s", packet.GetSourcePort(), packet.GetSourceChannel(),
   111  		)
   112  	}
   113  
   114  	if packet.GetSequence() != nextSequenceSend {
   115  		return sdkerrors.Wrapf(
   116  			types.ErrInvalidPacket,
   117  			"packet sequence ≠ next send sequence (%d ≠ %d)", packet.GetSequence(), nextSequenceSend,
   118  		)
   119  	}
   120  
   121  	commitment := types.CommitPacket(k.cdc, packet)
   122  
   123  	nextSequenceSend++
   124  	k.SetNextSequenceSend(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), nextSequenceSend)
   125  	k.SetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), commitment)
   126  
   127  	EmitSendPacketEvent(ctx, packet, channel, timeoutHeight)
   128  
   129  	k.Logger(ctx).Info(
   130  		"packet sent",
   131  		"sequence", packet.GetSequence(),
   132  		"src_port", packet.GetSourcePort(),
   133  		"src_channel", packet.GetSourceChannel(),
   134  		"dst_port", packet.GetDestPort(),
   135  		"dst_channel", packet.GetDestChannel(),
   136  	)
   137  
   138  	return nil
   139  }
   140  
   141  // RecvPacket is called by a module in order to receive & process an IBC packet
   142  // sent on the corresponding channel end on the counterparty chain.
   143  func (k Keeper) RecvPacket(
   144  	ctx sdk.Context,
   145  	chanCap *capabilitytypes.Capability,
   146  	packet exported.PacketI,
   147  	proof []byte,
   148  	proofHeight exported.Height,
   149  ) error {
   150  	channel, found := k.GetChannel(ctx, packet.GetDestPort(), packet.GetDestChannel())
   151  	if !found {
   152  		return sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetDestChannel())
   153  	}
   154  
   155  	if channel.State != types.OPEN {
   156  		return sdkerrors.Wrapf(
   157  			types.ErrInvalidChannelState,
   158  			"channel state is not OPEN (got %s)", channel.State.String(),
   159  		)
   160  	}
   161  
   162  	// Authenticate capability to ensure caller has authority to receive packet on this channel
   163  	capName := host.ChannelCapabilityPath(packet.GetDestPort(), packet.GetDestChannel())
   164  	if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) {
   165  		return sdkerrors.Wrapf(
   166  			types.ErrInvalidChannelCapability,
   167  			"channel capability failed authentication for capability name %s", capName,
   168  		)
   169  	}
   170  
   171  	// packet must come from the channel's counterparty
   172  	if packet.GetSourcePort() != channel.Counterparty.PortId {
   173  		return sdkerrors.Wrapf(
   174  			types.ErrInvalidPacket,
   175  			"packet source port doesn't match the counterparty's port (%s ≠ %s)", packet.GetSourcePort(), channel.Counterparty.PortId,
   176  		)
   177  	}
   178  
   179  	if packet.GetSourceChannel() != channel.Counterparty.ChannelId {
   180  		return sdkerrors.Wrapf(
   181  			types.ErrInvalidPacket,
   182  			"packet source channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetSourceChannel(), channel.Counterparty.ChannelId,
   183  		)
   184  	}
   185  
   186  	// Connection must be OPEN to receive a packet. It is possible for connection to not yet be open if packet was
   187  	// sent optimistically before connection and channel handshake completed. However, to receive a packet,
   188  	// connection and channel must both be open
   189  	connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0])
   190  	if !found {
   191  		return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0])
   192  	}
   193  
   194  	if connectionEnd.GetState() != int32(connectiontypes.OPEN) {
   195  		return sdkerrors.Wrapf(
   196  			connectiontypes.ErrInvalidConnectionState,
   197  			"connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(),
   198  		)
   199  	}
   200  
   201  	// check if packet timeouted by comparing it with the latest height of the chain
   202  	selfHeight := clienttypes.GetSelfHeight(ctx)
   203  	timeoutHeight := packet.GetTimeoutHeight()
   204  	if !timeoutHeight.IsZero() && selfHeight.GTE(timeoutHeight) {
   205  		return sdkerrors.Wrapf(
   206  			types.ErrPacketTimeout,
   207  			"block height >= packet timeout height (%s >= %s)", selfHeight, timeoutHeight,
   208  		)
   209  	}
   210  
   211  	// check if packet timeouted by comparing it with the latest timestamp of the chain
   212  	if packet.GetTimeoutTimestamp() != 0 && uint64(ctx.BlockTime().UnixNano()) >= packet.GetTimeoutTimestamp() {
   213  		return sdkerrors.Wrapf(
   214  			types.ErrPacketTimeout,
   215  			"block timestamp >= packet timeout timestamp (%s >= %s)", ctx.BlockTime(), time.Unix(0, int64(packet.GetTimeoutTimestamp())),
   216  		)
   217  	}
   218  
   219  	commitment := types.CommitPacket(k.cdc, packet)
   220  
   221  	// verify that the counterparty did commit to sending this packet
   222  	if err := k.connectionKeeper.VerifyPacketCommitment(
   223  		ctx, connectionEnd, proofHeight, proof,
   224  		packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(),
   225  		commitment,
   226  	); err != nil {
   227  		return sdkerrors.Wrap(err, "couldn't verify counterparty packet commitment")
   228  	}
   229  
   230  	switch channel.Ordering {
   231  	case types.UNORDERED:
   232  		// check if the packet receipt has been received already for unordered channels
   233  		_, found := k.GetPacketReceipt(ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
   234  		if found {
   235  			EmitRecvPacketEvent(ctx, packet, channel)
   236  			// This error indicates that the packet has already been relayed. Core IBC will
   237  			// treat this error as a no-op in order to prevent an entire relay transaction
   238  			// from failing and consuming unnecessary fees.
   239  			return types.ErrNoOpMsg
   240  		}
   241  
   242  		// All verification complete, update state
   243  		// For unordered channels we must set the receipt so it can be verified on the other side.
   244  		// This receipt does not contain any data, since the packet has not yet been processed,
   245  		// it's just a single store key set to an empty string to indicate that the packet has been received
   246  		k.SetPacketReceipt(ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
   247  
   248  	case types.ORDERED:
   249  		// check if the packet is being received in order
   250  		nextSequenceRecv, found := k.GetNextSequenceRecv(ctx, packet.GetDestPort(), packet.GetDestChannel())
   251  		if !found {
   252  			return sdkerrors.Wrapf(
   253  				types.ErrSequenceReceiveNotFound,
   254  				"destination port: %s, destination channel: %s", packet.GetDestPort(), packet.GetDestChannel(),
   255  			)
   256  		}
   257  
   258  		if packet.GetSequence() < nextSequenceRecv {
   259  			EmitRecvPacketEvent(ctx, packet, channel)
   260  			// This error indicates that the packet has already been relayed. Core IBC will
   261  			// treat this error as a no-op in order to prevent an entire relay transaction
   262  			// from failing and consuming unnecessary fees.
   263  			return types.ErrNoOpMsg
   264  		}
   265  
   266  		if packet.GetSequence() != nextSequenceRecv {
   267  			return sdkerrors.Wrapf(
   268  				types.ErrPacketSequenceOutOfOrder,
   269  				"packet sequence ≠ next receive sequence (%d ≠ %d)", packet.GetSequence(), nextSequenceRecv,
   270  			)
   271  		}
   272  
   273  		// All verification complete, update state
   274  		// In ordered case, we must increment nextSequenceRecv
   275  		nextSequenceRecv++
   276  
   277  		// incrementing nextSequenceRecv and storing under this chain's channelEnd identifiers
   278  		// Since this is the receiving chain, our channelEnd is packet's destination port and channel
   279  		k.SetNextSequenceRecv(ctx, packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv)
   280  
   281  	}
   282  
   283  	// log that a packet has been received & executed
   284  	k.Logger(ctx).Info(
   285  		"packet received",
   286  		"sequence", packet.GetSequence(),
   287  		"src_port", packet.GetSourcePort(),
   288  		"src_channel", packet.GetSourceChannel(),
   289  		"dst_port", packet.GetDestPort(),
   290  		"dst_channel", packet.GetDestChannel(),
   291  	)
   292  
   293  	// emit an event that the relayer can query for
   294  	EmitRecvPacketEvent(ctx, packet, channel)
   295  
   296  	return nil
   297  }
   298  
   299  // WriteAcknowledgement writes the packet execution acknowledgement to the state,
   300  // which will be verified by the counterparty chain using AcknowledgePacket.
   301  //
   302  // CONTRACT:
   303  //
   304  // 1) For synchronous execution, this function is be called in the IBC handler .
   305  // For async handling, it needs to be called directly by the module which originally
   306  // processed the packet.
   307  //
   308  // 2) Assumes that packet receipt has been written (unordered), or nextSeqRecv was incremented (ordered)
   309  // previously by RecvPacket.
   310  func (k Keeper) WriteAcknowledgement(
   311  	ctx sdk.Context,
   312  	chanCap *capabilitytypes.Capability,
   313  	packet exported.PacketI,
   314  	acknowledgement exported.Acknowledgement,
   315  ) error {
   316  	channel, found := k.GetChannel(ctx, packet.GetDestPort(), packet.GetDestChannel())
   317  	if !found {
   318  		return sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetDestChannel())
   319  	}
   320  
   321  	if channel.State != types.OPEN {
   322  		return sdkerrors.Wrapf(
   323  			types.ErrInvalidChannelState,
   324  			"channel state is not OPEN (got %s)", channel.State.String(),
   325  		)
   326  	}
   327  
   328  	// Authenticate capability to ensure caller has authority to receive packet on this channel
   329  	capName := host.ChannelCapabilityPath(packet.GetDestPort(), packet.GetDestChannel())
   330  	if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) {
   331  		return sdkerrors.Wrapf(
   332  			types.ErrInvalidChannelCapability,
   333  			"channel capability failed authentication for capability name %s", capName,
   334  		)
   335  	}
   336  
   337  	// NOTE: IBC app modules might have written the acknowledgement synchronously on
   338  	// the OnRecvPacket callback so we need to check if the acknowledgement is already
   339  	// set on the store and return an error if so.
   340  	if k.HasPacketAcknowledgement(ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) {
   341  		return types.ErrAcknowledgementExists
   342  	}
   343  
   344  	if acknowledgement == nil {
   345  		return sdkerrors.Wrap(types.ErrInvalidAcknowledgement, "acknowledgement cannot be nil")
   346  	}
   347  
   348  	bz := acknowledgement.Acknowledgement()
   349  	if len(bz) == 0 {
   350  		return sdkerrors.Wrap(types.ErrInvalidAcknowledgement, "acknowledgement cannot be empty")
   351  	}
   352  
   353  	// set the acknowledgement so that it can be verified on the other side
   354  	k.SetPacketAcknowledgement(
   355  		ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(),
   356  		types.CommitAcknowledgement(bz),
   357  	)
   358  
   359  	// log that a packet acknowledgement has been written
   360  	k.Logger(ctx).Info(
   361  		"acknowledgement written",
   362  		"sequence", packet.GetSequence(),
   363  		"src_port", packet.GetSourcePort(),
   364  		"src_channel", packet.GetSourceChannel(),
   365  		"dst_port", packet.GetDestPort(),
   366  		"dst_channel", packet.GetDestChannel(),
   367  	)
   368  
   369  	EmitWriteAcknowledgementEvent(ctx, packet, channel, bz)
   370  
   371  	return nil
   372  }
   373  
   374  // AcknowledgePacket is called by a module to process the acknowledgement of a
   375  // packet previously sent by the calling module on a channel to a counterparty
   376  // module on the counterparty chain. Its intended usage is within the ante
   377  // handler. AcknowledgePacket will clean up the packet commitment,
   378  // which is no longer necessary since the packet has been received and acted upon.
   379  // It will also increment NextSequenceAck in case of ORDERED channels.
   380  func (k Keeper) AcknowledgePacket(
   381  	ctx sdk.Context,
   382  	chanCap *capabilitytypes.Capability,
   383  	packet exported.PacketI,
   384  	acknowledgement []byte,
   385  	proof []byte,
   386  	proofHeight exported.Height,
   387  ) error {
   388  	channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel())
   389  	if !found {
   390  		return sdkerrors.Wrapf(
   391  			types.ErrChannelNotFound,
   392  			"port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel(),
   393  		)
   394  	}
   395  
   396  	if channel.State != types.OPEN {
   397  		return sdkerrors.Wrapf(
   398  			types.ErrInvalidChannelState,
   399  			"channel state is not OPEN (got %s)", channel.State.String(),
   400  		)
   401  	}
   402  
   403  	// Authenticate capability to ensure caller has authority to receive packet on this channel
   404  	capName := host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel())
   405  	if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) {
   406  		return sdkerrors.Wrapf(
   407  			types.ErrInvalidChannelCapability,
   408  			"channel capability failed authentication for capability name %s", capName,
   409  		)
   410  	}
   411  
   412  	// packet must have been sent to the channel's counterparty
   413  	if packet.GetDestPort() != channel.Counterparty.PortId {
   414  		return sdkerrors.Wrapf(
   415  			types.ErrInvalidPacket,
   416  			"packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortId,
   417  		)
   418  	}
   419  
   420  	if packet.GetDestChannel() != channel.Counterparty.ChannelId {
   421  		return sdkerrors.Wrapf(
   422  			types.ErrInvalidPacket,
   423  			"packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelId,
   424  		)
   425  	}
   426  
   427  	connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0])
   428  	if !found {
   429  		return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0])
   430  	}
   431  
   432  	if connectionEnd.GetState() != int32(connectiontypes.OPEN) {
   433  		return sdkerrors.Wrapf(
   434  			connectiontypes.ErrInvalidConnectionState,
   435  			"connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(),
   436  		)
   437  	}
   438  
   439  	commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
   440  
   441  	if len(commitment) == 0 {
   442  		EmitAcknowledgePacketEvent(ctx, packet, channel)
   443  		// This error indicates that the acknowledgement has already been relayed
   444  		// or there is a misconfigured relayer attempting to prove an acknowledgement
   445  		// for a packet never sent. Core IBC will treat this error as a no-op in order to
   446  		// prevent an entire relay transaction from failing and consuming unnecessary fees.
   447  		return types.ErrNoOpMsg
   448  	}
   449  
   450  	packetCommitment := types.CommitPacket(k.cdc, packet)
   451  
   452  	// verify we sent the packet and haven't cleared it out yet
   453  	if !bytes.Equal(commitment, packetCommitment) {
   454  		return sdkerrors.Wrapf(types.ErrInvalidPacket, "commitment bytes are not equal: got (%v), expected (%v)", packetCommitment, commitment)
   455  	}
   456  
   457  	if err := k.connectionKeeper.VerifyPacketAcknowledgement(
   458  		ctx, connectionEnd, proofHeight, proof, packet.GetDestPort(), packet.GetDestChannel(),
   459  		packet.GetSequence(), acknowledgement,
   460  	); err != nil {
   461  		return err
   462  	}
   463  
   464  	// assert packets acknowledged in order
   465  	if channel.Ordering == types.ORDERED {
   466  		nextSequenceAck, found := k.GetNextSequenceAck(ctx, packet.GetSourcePort(), packet.GetSourceChannel())
   467  		if !found {
   468  			return sdkerrors.Wrapf(
   469  				types.ErrSequenceAckNotFound,
   470  				"source port: %s, source channel: %s", packet.GetSourcePort(), packet.GetSourceChannel(),
   471  			)
   472  		}
   473  
   474  		if packet.GetSequence() != nextSequenceAck {
   475  			return sdkerrors.Wrapf(
   476  				types.ErrPacketSequenceOutOfOrder,
   477  				"packet sequence ≠ next ack sequence (%d ≠ %d)", packet.GetSequence(), nextSequenceAck,
   478  			)
   479  		}
   480  
   481  		// All verification complete, in the case of ORDERED channels we must increment nextSequenceAck
   482  		nextSequenceAck++
   483  
   484  		// incrementing NextSequenceAck and storing under this chain's channelEnd identifiers
   485  		// Since this is the original sending chain, our channelEnd is packet's source port and channel
   486  		k.SetNextSequenceAck(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), nextSequenceAck)
   487  
   488  	}
   489  
   490  	// Delete packet commitment, since the packet has been acknowledged, the commitement is no longer necessary
   491  	k.deletePacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
   492  
   493  	// log that a packet has been acknowledged
   494  	k.Logger(ctx).Info(
   495  		"packet acknowledged",
   496  		"sequence", packet.GetSequence(),
   497  		"src_port", packet.GetSourcePort(),
   498  		"src_channel", packet.GetSourceChannel(),
   499  		"dst_port", packet.GetDestPort(),
   500  		"dst_channel", packet.GetDestChannel(),
   501  	)
   502  
   503  	// emit an event marking that we have processed the acknowledgement
   504  	EmitAcknowledgePacketEvent(ctx, packet, channel)
   505  
   506  	return nil
   507  }