491 lines
18 KiB
Go
491 lines
18 KiB
Go
package keeper
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"time"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
"github.com/cosmos/cosmos-sdk/x/capability"
|
|
client "github.com/cosmos/cosmos-sdk/x/ibc/02-client"
|
|
connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection"
|
|
connectionexported "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/exported"
|
|
"github.com/cosmos/cosmos-sdk/x/ibc/04-channel/exported"
|
|
"github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
|
|
commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported"
|
|
ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types"
|
|
)
|
|
|
|
// SendPacket is called by a module in order to send an IBC packet on a channel
|
|
// end owned by the calling module to the corresponding module on the counterparty
|
|
// chain.
|
|
func (k Keeper) SendPacket(
|
|
ctx sdk.Context,
|
|
channelCap *capability.Capability,
|
|
packet exported.PacketI,
|
|
) error {
|
|
if err := packet.ValidateBasic(); err != nil {
|
|
return err
|
|
}
|
|
|
|
channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel())
|
|
if !found {
|
|
return sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetSourceChannel())
|
|
}
|
|
|
|
if channel.State == exported.CLOSED {
|
|
return sdkerrors.Wrapf(
|
|
types.ErrInvalidChannelState,
|
|
"channel is CLOSED (got %s)", channel.State.String(),
|
|
)
|
|
}
|
|
|
|
if !k.scopedKeeper.AuthenticateCapability(ctx, channelCap, ibctypes.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel())) {
|
|
return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel")
|
|
}
|
|
|
|
if packet.GetDestPort() != channel.Counterparty.PortID {
|
|
return sdkerrors.Wrapf(
|
|
types.ErrInvalidPacket,
|
|
"packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortID,
|
|
)
|
|
}
|
|
|
|
if packet.GetDestChannel() != channel.Counterparty.ChannelID {
|
|
return sdkerrors.Wrapf(
|
|
types.ErrInvalidPacket,
|
|
"packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelID,
|
|
)
|
|
}
|
|
|
|
connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0])
|
|
if !found {
|
|
return sdkerrors.Wrap(connection.ErrConnectionNotFound, channel.ConnectionHops[0])
|
|
}
|
|
|
|
// NOTE: assume UNINITIALIZED is a closed connection
|
|
if connectionEnd.GetState() == connectionexported.UNINITIALIZED {
|
|
return sdkerrors.Wrap(
|
|
connection.ErrInvalidConnectionState,
|
|
"connection is closed (i.e NONE)",
|
|
)
|
|
}
|
|
|
|
clientState, found := k.clientKeeper.GetClientState(ctx, connectionEnd.GetClientID())
|
|
if !found {
|
|
return client.ErrConsensusStateNotFound
|
|
}
|
|
|
|
// check if packet timeouted on the receiving chain
|
|
latestHeight := clientState.GetLatestHeight()
|
|
if packet.GetTimeoutHeight() != 0 && latestHeight >= packet.GetTimeoutHeight() {
|
|
return sdkerrors.Wrapf(
|
|
types.ErrPacketTimeout,
|
|
"receiving chain block height >= packet timeout height (%d >= %d)", latestHeight, packet.GetTimeoutHeight(),
|
|
)
|
|
}
|
|
|
|
latestTimestamp, err := k.connectionKeeper.GetTimestampAtHeight(ctx, connectionEnd, latestHeight)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if packet.GetTimeoutTimestamp() != 0 && latestTimestamp >= packet.GetTimeoutTimestamp() {
|
|
return sdkerrors.Wrapf(
|
|
types.ErrPacketTimeout,
|
|
"receiving chain block timestamp >= packet timeout timestamp (%s >= %s)", time.Unix(0, int64(latestTimestamp)), time.Unix(0, int64(packet.GetTimeoutTimestamp())),
|
|
)
|
|
}
|
|
|
|
nextSequenceSend, found := k.GetNextSequenceSend(ctx, packet.GetSourcePort(), packet.GetSourceChannel())
|
|
if !found {
|
|
return types.ErrSequenceSendNotFound
|
|
}
|
|
|
|
if packet.GetSequence() != nextSequenceSend {
|
|
return sdkerrors.Wrapf(
|
|
types.ErrInvalidPacket,
|
|
"packet sequence ≠ next send sequence (%d ≠ %d)", packet.GetSequence(), nextSequenceSend,
|
|
)
|
|
}
|
|
|
|
nextSequenceSend++
|
|
k.SetNextSequenceSend(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), nextSequenceSend)
|
|
k.SetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), types.CommitPacket(packet))
|
|
|
|
// Emit Event with Packet data along with other packet information for relayer to pick up
|
|
// and relay to other chain
|
|
ctx.EventManager().EmitEvents(sdk.Events{
|
|
sdk.NewEvent(
|
|
types.EventTypeSendPacket,
|
|
sdk.NewAttribute(types.AttributeKeyData, string(packet.GetData())),
|
|
sdk.NewAttribute(types.AttributeKeyTimeoutHeight, fmt.Sprintf("%d", packet.GetTimeoutHeight())),
|
|
sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())),
|
|
sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())),
|
|
sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()),
|
|
sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()),
|
|
sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()),
|
|
sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()),
|
|
),
|
|
})
|
|
|
|
k.Logger(ctx).Info(fmt.Sprintf("packet sent: %v", packet))
|
|
return nil
|
|
}
|
|
|
|
// RecvPacket is called by a module in order to receive & process an IBC packet
|
|
// sent on the corresponding channel end on the counterparty chain.
|
|
func (k Keeper) RecvPacket(
|
|
ctx sdk.Context,
|
|
packet exported.PacketI,
|
|
proof commitmentexported.Proof,
|
|
proofHeight uint64,
|
|
) (exported.PacketI, error) {
|
|
channel, found := k.GetChannel(ctx, packet.GetDestPort(), packet.GetDestChannel())
|
|
if !found {
|
|
return nil, sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetDestChannel())
|
|
}
|
|
|
|
if channel.State != exported.OPEN {
|
|
return nil, sdkerrors.Wrapf(
|
|
types.ErrInvalidChannelState,
|
|
"channel state is not OPEN (got %s)", channel.State.String(),
|
|
)
|
|
}
|
|
|
|
// NOTE: RecvPacket is called by the AnteHandler which acts upon the packet.Route(),
|
|
// so the capability authentication can be omitted here
|
|
|
|
// packet must come from the channel's counterparty
|
|
if packet.GetSourcePort() != channel.Counterparty.PortID {
|
|
return nil, sdkerrors.Wrapf(
|
|
types.ErrInvalidPacket,
|
|
"packet source port doesn't match the counterparty's port (%s ≠ %s)", packet.GetSourcePort(), channel.Counterparty.PortID,
|
|
)
|
|
}
|
|
|
|
if packet.GetSourceChannel() != channel.Counterparty.ChannelID {
|
|
return nil, sdkerrors.Wrapf(
|
|
types.ErrInvalidPacket,
|
|
"packet source channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetSourceChannel(), channel.Counterparty.ChannelID,
|
|
)
|
|
}
|
|
|
|
connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0])
|
|
if !found {
|
|
return nil, sdkerrors.Wrap(connection.ErrConnectionNotFound, channel.ConnectionHops[0])
|
|
}
|
|
|
|
if connectionEnd.GetState() != connectionexported.OPEN {
|
|
return nil, sdkerrors.Wrapf(
|
|
connection.ErrInvalidConnectionState,
|
|
"connection state is not OPEN (got %s)", connectionEnd.GetState().String(),
|
|
)
|
|
}
|
|
|
|
// check if packet timeouted by comparing it with the latest height of the chain
|
|
if packet.GetTimeoutHeight() != 0 && uint64(ctx.BlockHeight()) >= packet.GetTimeoutHeight() {
|
|
return nil, sdkerrors.Wrapf(
|
|
types.ErrPacketTimeout,
|
|
"block height >= packet timeout height (%d >= %d)", uint64(ctx.BlockHeight()), packet.GetTimeoutHeight(),
|
|
)
|
|
}
|
|
|
|
// check if packet timeouted by comparing it with the latest timestamp of the chain
|
|
if packet.GetTimeoutTimestamp() != 0 && uint64(ctx.BlockTime().UnixNano()) >= packet.GetTimeoutTimestamp() {
|
|
return nil, sdkerrors.Wrapf(
|
|
types.ErrPacketTimeout,
|
|
"block timestamp >= packet timeout timestamp (%s >= %s)", ctx.BlockTime(), time.Unix(0, int64(packet.GetTimeoutTimestamp())),
|
|
)
|
|
}
|
|
|
|
if err := k.connectionKeeper.VerifyPacketCommitment(
|
|
ctx, connectionEnd, proofHeight, proof,
|
|
packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(),
|
|
types.CommitPacket(packet),
|
|
); err != nil {
|
|
return nil, sdkerrors.Wrap(err, "couldn't verify counterparty packet commitment")
|
|
}
|
|
|
|
return packet, nil
|
|
}
|
|
|
|
// PacketExecuted writes the packet execution acknowledgement to the state,
|
|
// which will be verified by the counterparty chain using AcknowledgePacket.
|
|
// CONTRACT: each packet handler function should call WriteAcknowledgement at the end of the execution
|
|
func (k Keeper) PacketExecuted(
|
|
ctx sdk.Context,
|
|
chanCap *capability.Capability,
|
|
packet exported.PacketI,
|
|
acknowledgement []byte,
|
|
) error {
|
|
channel, found := k.GetChannel(ctx, packet.GetDestPort(), packet.GetDestChannel())
|
|
if !found {
|
|
return sdkerrors.Wrapf(types.ErrChannelNotFound, packet.GetDestChannel())
|
|
}
|
|
|
|
// sanity check
|
|
if channel.State != exported.OPEN {
|
|
return sdkerrors.Wrapf(
|
|
types.ErrInvalidChannelState,
|
|
"channel state is not OPEN (got %s)", channel.State.String(),
|
|
)
|
|
}
|
|
|
|
capName := ibctypes.ChannelCapabilityPath(packet.GetDestPort(), packet.GetDestChannel())
|
|
if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) {
|
|
return sdkerrors.Wrap(types.ErrInvalidChannelCapability, "channel capability failed authentication")
|
|
}
|
|
|
|
if acknowledgement != nil || channel.Ordering == exported.UNORDERED {
|
|
k.SetPacketAcknowledgement(
|
|
ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(),
|
|
types.CommitAcknowledgement(acknowledgement),
|
|
)
|
|
}
|
|
|
|
if channel.Ordering == exported.ORDERED {
|
|
nextSequenceRecv, found := k.GetNextSequenceRecv(ctx, packet.GetDestPort(), packet.GetDestChannel())
|
|
if !found {
|
|
return types.ErrSequenceReceiveNotFound
|
|
}
|
|
|
|
if packet.GetSequence() != nextSequenceRecv {
|
|
return sdkerrors.Wrapf(
|
|
types.ErrInvalidPacket,
|
|
"packet sequence ≠ next receive sequence (%d ≠ %d)", packet.GetSequence(), nextSequenceRecv,
|
|
)
|
|
}
|
|
|
|
nextSequenceRecv++
|
|
|
|
k.SetNextSequenceRecv(ctx, packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv)
|
|
}
|
|
|
|
// log that a packet has been received & executed
|
|
k.Logger(ctx).Info(fmt.Sprintf("packet received & executed: %v", packet))
|
|
|
|
// emit an event that the relayer can query for
|
|
ctx.EventManager().EmitEvents(sdk.Events{
|
|
sdk.NewEvent(
|
|
types.EventTypeRecvPacket,
|
|
sdk.NewAttribute(types.AttributeKeyData, string(packet.GetData())),
|
|
sdk.NewAttribute(types.AttributeKeyAck, string(acknowledgement)),
|
|
sdk.NewAttribute(types.AttributeKeyTimeoutHeight, fmt.Sprintf("%d", packet.GetTimeoutHeight())),
|
|
sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())),
|
|
sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())),
|
|
sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()),
|
|
sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()),
|
|
sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()),
|
|
sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()),
|
|
),
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// AcknowledgePacket is called by a module to process the acknowledgement of a
|
|
// packet previously sent by the calling module on a channel to a counterparty
|
|
// module on the counterparty chain. acknowledgePacket also cleans up the packet
|
|
// commitment, which is no longer necessary since the packet has been received
|
|
// and acted upon.
|
|
func (k Keeper) AcknowledgePacket(
|
|
ctx sdk.Context,
|
|
packet exported.PacketI,
|
|
acknowledgement []byte,
|
|
proof commitmentexported.Proof,
|
|
proofHeight uint64,
|
|
) (exported.PacketI, error) {
|
|
channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel())
|
|
if !found {
|
|
return nil, sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetSourceChannel())
|
|
}
|
|
|
|
if channel.State != exported.OPEN {
|
|
return nil, sdkerrors.Wrapf(
|
|
types.ErrInvalidChannelState,
|
|
"channel state is not OPEN (got %s)", channel.State.String(),
|
|
)
|
|
}
|
|
|
|
// NOTE: RecvPacket is called by the AnteHandler which acts upon the packet.Route(),
|
|
// so the capability authentication can be omitted here
|
|
|
|
// packet must have been sent to the channel's counterparty
|
|
if packet.GetDestPort() != channel.Counterparty.PortID {
|
|
return nil, sdkerrors.Wrapf(
|
|
types.ErrInvalidPacket,
|
|
"packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortID,
|
|
)
|
|
}
|
|
|
|
if packet.GetDestChannel() != channel.Counterparty.ChannelID {
|
|
return nil, sdkerrors.Wrapf(
|
|
types.ErrInvalidPacket,
|
|
"packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelID,
|
|
)
|
|
}
|
|
|
|
connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0])
|
|
if !found {
|
|
return nil, sdkerrors.Wrap(connection.ErrConnectionNotFound, channel.ConnectionHops[0])
|
|
}
|
|
|
|
if connectionEnd.GetState() != connectionexported.OPEN {
|
|
return nil, sdkerrors.Wrapf(
|
|
connection.ErrInvalidConnectionState,
|
|
"connection state is not OPEN (got %s)", connectionEnd.GetState().String(),
|
|
)
|
|
}
|
|
|
|
commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
|
|
|
|
// verify we sent the packet and haven't cleared it out yet
|
|
if !bytes.Equal(commitment, types.CommitPacket(packet)) {
|
|
return nil, sdkerrors.Wrap(types.ErrInvalidPacket, "packet hasn't been sent")
|
|
}
|
|
|
|
if err := k.connectionKeeper.VerifyPacketAcknowledgement(
|
|
ctx, connectionEnd, proofHeight, proof, packet.GetDestPort(), packet.GetDestChannel(),
|
|
packet.GetSequence(), acknowledgement,
|
|
); err != nil {
|
|
return nil, sdkerrors.Wrap(err, "invalid acknowledgement on counterparty chain")
|
|
}
|
|
|
|
// log that a packet has been acknowledged
|
|
k.Logger(ctx).Info(fmt.Sprintf("packet acknowledged: %v", packet))
|
|
|
|
// emit an event marking that we have processed the acknowledgement
|
|
ctx.EventManager().EmitEvents(sdk.Events{
|
|
sdk.NewEvent(
|
|
types.EventTypeAcknowledgePacket,
|
|
sdk.NewAttribute(types.AttributeKeyTimeoutHeight, fmt.Sprintf("%d", packet.GetTimeoutHeight())),
|
|
sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())),
|
|
sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())),
|
|
sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()),
|
|
sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()),
|
|
sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()),
|
|
sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()),
|
|
),
|
|
})
|
|
|
|
return packet, nil
|
|
}
|
|
|
|
// CleanupPacket is called by a module to remove a received packet commitment
|
|
// from storage. The receiving end must have already processed the packet
|
|
// (whether regularly or past timeout).
|
|
//
|
|
// In the ORDERED channel case, CleanupPacket cleans-up a packet on an ordered
|
|
// channel by proving that the packet has been received on the other end.
|
|
//
|
|
// In the UNORDERED channel case, CleanupPacket cleans-up a packet on an
|
|
// unordered channel by proving that the associated acknowledgement has been
|
|
//written.
|
|
func (k Keeper) CleanupPacket(
|
|
ctx sdk.Context,
|
|
packet exported.PacketI,
|
|
proof commitmentexported.Proof,
|
|
proofHeight,
|
|
nextSequenceRecv uint64,
|
|
acknowledgement []byte,
|
|
) (exported.PacketI, error) {
|
|
channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel())
|
|
if !found {
|
|
return nil, sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetSourceChannel())
|
|
}
|
|
|
|
if channel.State != exported.OPEN {
|
|
return nil, sdkerrors.Wrapf(
|
|
types.ErrInvalidChannelState,
|
|
"channel state is not OPEN (got %s)", channel.State.String(),
|
|
)
|
|
}
|
|
|
|
// TODO: blocked by #5542
|
|
// capKey, found := k.GetChannelCapability(ctx, packet.GetSourcePort(), packet.GetSourceChannel())
|
|
// if !found {
|
|
// return nil, types.ErrChannelCapabilityNotFound
|
|
// }
|
|
|
|
// portCapabilityKey := sdk.NewKVStoreKey(capKey)
|
|
|
|
// if !k.portKeeper.Authenticate(portCapabilityKey, packet.GetSourcePort()) {
|
|
// return nil, sdkerrors.Wrapf(port.ErrInvalidPort, "invalid source port: %s", packet.GetSourcePort())
|
|
// }
|
|
|
|
if packet.GetDestPort() != channel.Counterparty.PortID {
|
|
return nil, sdkerrors.Wrapf(types.ErrInvalidPacket,
|
|
"packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortID,
|
|
)
|
|
}
|
|
|
|
if packet.GetDestChannel() != channel.Counterparty.ChannelID {
|
|
return nil, sdkerrors.Wrapf(
|
|
types.ErrInvalidPacket,
|
|
"packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelID,
|
|
)
|
|
}
|
|
|
|
connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0])
|
|
if !found {
|
|
return nil, sdkerrors.Wrap(connection.ErrConnectionNotFound, channel.ConnectionHops[0])
|
|
}
|
|
|
|
// check that packet has been received on the other end
|
|
if nextSequenceRecv <= packet.GetSequence() {
|
|
return nil, sdkerrors.Wrap(types.ErrInvalidPacket, "packet already received")
|
|
}
|
|
|
|
commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
|
|
|
|
// verify we sent the packet and haven't cleared it out yet
|
|
if !bytes.Equal(commitment, types.CommitPacket(packet)) {
|
|
return nil, sdkerrors.Wrap(types.ErrInvalidPacket, "packet hasn't been sent")
|
|
}
|
|
|
|
var err error
|
|
switch channel.Ordering {
|
|
case exported.ORDERED:
|
|
// check that the recv sequence is as claimed
|
|
err = k.connectionKeeper.VerifyNextSequenceRecv(
|
|
ctx, connectionEnd, proofHeight, proof,
|
|
packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv,
|
|
)
|
|
case exported.UNORDERED:
|
|
err = k.connectionKeeper.VerifyPacketAcknowledgement(
|
|
ctx, connectionEnd, proofHeight, proof,
|
|
packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(),
|
|
acknowledgement,
|
|
)
|
|
default:
|
|
panic(sdkerrors.Wrapf(types.ErrInvalidChannelOrdering, channel.Ordering.String()))
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, sdkerrors.Wrap(err, "packet verification failed")
|
|
}
|
|
|
|
k.deletePacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
|
|
|
|
// log that a packet has been acknowledged
|
|
k.Logger(ctx).Info(fmt.Sprintf("packet cleaned-up: %v", packet))
|
|
|
|
// emit an event marking that we have cleaned up the packet
|
|
ctx.EventManager().EmitEvents(sdk.Events{
|
|
sdk.NewEvent(
|
|
types.EventTypeCleanupPacket,
|
|
sdk.NewAttribute(types.AttributeKeyTimeoutHeight, fmt.Sprintf("%d", packet.GetTimeoutHeight())),
|
|
sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())),
|
|
sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())),
|
|
sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()),
|
|
sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()),
|
|
sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()),
|
|
sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()),
|
|
),
|
|
})
|
|
|
|
return packet, nil
|
|
}
|