Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify WriteAck API for async acks #7869

Merged
merged 6 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions modules/core/04-channel/v2/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,35 @@ func (k *Keeper) SetNextSequenceSend(ctx context.Context, clientID string, seque
panic(err)
}
}

// SetAsyncPacket writes the packet under the async path
func (k *Keeper) SetAsyncPacket(ctx context.Context, clientID string, sequence uint64, packet types.Packet) {
store := k.KVStoreService.OpenKVStore(ctx)
bz := k.cdc.MustMarshal(&packet)
if err := store.Set(types.AsyncPacketKey(clientID, sequence), bz); err != nil {
panic(err)
}
}

// GetAsyncPacket fetches the packet from the async path
func (k *Keeper) GetAsyncPacket(ctx context.Context, clientID string, sequence uint64) (types.Packet, bool) {
store := k.KVStoreService.OpenKVStore(ctx)
bz, err := store.Get(types.AsyncPacketKey(clientID, sequence))
if err != nil {
panic(err)
}
if len(bz) == 0 {
return types.Packet{}, false
}
var packet types.Packet
k.cdc.MustUnmarshal(bz, &packet)
return packet, true
}

// DeleteAsyncPacket deletes the packet from the async path
func (k *Keeper) DeleteAsyncPacket(ctx context.Context, clientID string, sequence uint64) {
store := k.KVStoreService.OpenKVStore(ctx)
if err := store.Delete(types.AsyncPacketKey(clientID, sequence)); err != nil {
panic(err)
}
}
5 changes: 4 additions & 1 deletion modules/core/04-channel/v2/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,12 @@ func (k *Keeper) RecvPacket(ctx context.Context, msg *types.MsgRecvPacket) (*typ
// Set packet acknowledgement only if the acknowledgement is not async.
// NOTE: IBC applications modules may call the WriteAcknowledgement asynchronously if the
// acknowledgement is async.
if err := k.WriteAcknowledgement(ctx, msg.Packet, ack); err != nil {
if err := k.writeAcknowledgement(ctx, msg.Packet, ack); err != nil {
return nil, err
}
} else {
// store the packet temporarily until the application returns an acknowledgement
k.SetAsyncPacket(ctx, msg.Packet.DestinationClient, msg.Packet.Sequence, msg.Packet)
}

// TODO: store the packet for async applications to access if required.
Expand Down
34 changes: 30 additions & 4 deletions modules/core/04-channel/v2/keeper/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,9 @@ func (k *Keeper) recvPacket(
return nil
}

// WriteAcknowledgement writes the acknowledgement to the store.
// TODO: change this function to accept destPort, destChannel, sequence, ack
func (k Keeper) WriteAcknowledgement(
// writeAcknowledgement writes the acknowledgement to the store and emits the packet and acknowledgement
// for relayers to relay the acknowledgement to the counterparty chain.
func (k Keeper) writeAcknowledgement(
ctx context.Context,
packet types.Packet,
ack types.Acknowledgement,
Expand Down Expand Up @@ -186,7 +186,33 @@ func (k Keeper) WriteAcknowledgement(

emitWriteAcknowledgementEvents(ctx, packet, ack)

// TODO: delete the packet that has been stored in ibc-core.
return nil
}

// WriteAcknowledgement writes the acknowledgement and emits events for asynchronous acknowledgements
// this is the method to be called by external apps when they want to write an acknowledgement asyncrhonously
func (k *Keeper) WriteAcknowledgement(ctx context.Context, clientID string, sequence uint64, ack types.Acknowledgement) error {
sdkCtx := sdk.UnwrapSDKContext(ctx)

// Validate the acknowledgement
if err := ack.Validate(); err != nil {
sdkCtx.Logger().Error("write acknowledgement failed", "error", errorsmod.Wrap(err, "invalid acknowledgement"))
return errorsmod.Wrap(err, "invalid acknowledgement")
}

packet, ok := k.GetAsyncPacket(ctx, clientID, sequence)
if !ok {
return errorsmod.Wrapf(types.ErrInvalidAcknowledgement, "packet with clientID (%s) and sequence (%d) not found for async acknowledgement", clientID, sequence)
}

// Write the acknowledgement to the store
if err := k.writeAcknowledgement(ctx, packet, ack); err != nil {
sdkCtx.Logger().Error("write acknowledgement failed", "error", errorsmod.Wrap(err, "write acknowledgement failed"))
return errorsmod.Wrap(err, "write acknowledgement failed")
}

// Delete the packet from the async store
k.DeleteAsyncPacket(ctx, clientID, sequence)

return nil
}
Expand Down
4 changes: 3 additions & 1 deletion modules/core/04-channel/v2/keeper/packet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,11 +301,13 @@ func (suite *KeeperTestSuite) TestWriteAcknowledgement() {
AppAcknowledgements: [][]byte{mockv2.MockRecvPacketResult.Acknowledgement},
}

// mock receive with async acknowledgement
suite.chainB.App.GetIBCKeeper().ChannelKeeperV2.SetPacketReceipt(suite.chainB.GetContext(), packet.DestinationClient, packet.Sequence)
suite.chainB.App.GetIBCKeeper().ChannelKeeperV2.SetAsyncPacket(suite.chainB.GetContext(), packet.DestinationClient, packet.Sequence, packet)

tc.malleate()

err := suite.chainB.App.GetIBCKeeper().ChannelKeeperV2.WriteAcknowledgement(suite.chainB.GetContext(), packet, ack)
err := suite.chainB.App.GetIBCKeeper().ChannelKeeperV2.WriteAcknowledgement(suite.chainB.GetContext(), packet.DestinationClient, packet.Sequence, ack)

expPass := tc.expError == nil
if expPass {
Expand Down
11 changes: 11 additions & 0 deletions modules/core/04-channel/v2/types/keys.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
package types

import "fmt"

const (
// SubModuleName defines the channelv2 module name.
SubModuleName = "channelv2"

// KeyAsyncPacket defines the key to store the async packet.
KeyAsyncPacket = "async_packet"
)

// AsyncPacketKey returns the key under which the packet is stored
// if the receiving application returns an async acknowledgement.
func AsyncPacketKey(clientID string, sequence uint64) []byte {
return []byte(fmt.Sprintf("%s/%s/%d", KeyAsyncPacket, clientID, sequence))
}
Loading