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

fix: event caching for fee distribution #661

Merged
merged 11 commits into from
Jan 12, 2022
34 changes: 5 additions & 29 deletions modules/apps/29-fee/ibc_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,28 +212,16 @@ func (im IBCModule) OnAcknowledgementPacket(
}

packetId := channeltypes.NewPacketId(packet.SourceChannel, packet.SourcePort, packet.Sequence)
identifiedPacketFee, found := im.keeper.GetFeeInEscrow(ctx, packetId)

identifiedPacketFee, found := im.keeper.GetFeeInEscrow(ctx, packetId)
if !found {
// return underlying callback if no fee found for given packetID
return im.app.OnAcknowledgementPacket(ctx, packet, ack.Result, relayer)
}

// cache context before trying to distribute the fee
cacheCtx, writeFn := ctx.CacheContext()

forwardRelayer, _ := sdk.AccAddressFromBech32(ack.ForwardRelayerAddress)
refundAcc, _ := sdk.AccAddressFromBech32(identifiedPacketFee.RefundAddress)
im.keeper.DistributePacketFees(ctx, identifiedPacketFee.RefundAddress, ack.ForwardRelayerAddress, relayer, identifiedPacketFee)

err := im.keeper.DistributeFee(cacheCtx, refundAcc, forwardRelayer, relayer, packetId)

if err == nil {
// write the cache and then call underlying callback
writeFn()
// NOTE: The context returned by CacheContext() refers to a new EventManager, so it needs to explicitly set events to the original context.
ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events())
}
// otherwise discard cache and call underlying callback
// call underlying callback
return im.app.OnAcknowledgementPacket(ctx, packet, ack.Result, relayer)
}

Expand All @@ -251,25 +239,13 @@ func (im IBCModule) OnTimeoutPacket(
packetId := channeltypes.NewPacketId(packet.SourceChannel, packet.SourcePort, packet.Sequence)

identifiedPacketFee, found := im.keeper.GetFeeInEscrow(ctx, packetId)

if !found {
// return underlying callback if fee not found for given packetID
return im.app.OnTimeoutPacket(ctx, packet, relayer)
}

// cache context before trying to distribute the fee
cacheCtx, writeFn := ctx.CacheContext()

refundAcc, _ := sdk.AccAddressFromBech32(identifiedPacketFee.RefundAddress)
err := im.keeper.DistributeFeeTimeout(cacheCtx, refundAcc, relayer, packetId)

if err == nil {
// write the cache and then call underlying callback
writeFn()
// NOTE: The context returned by CacheContext() refers to a new EventManager, so it needs to explicitly set events to the original context.
ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events())
}
im.keeper.DistributePacketFeesOnTimeout(ctx, identifiedPacketFee.RefundAddress, relayer, identifiedPacketFee)

// otherwise discard cache and call underlying callback
// call underlying callback
return im.app.OnTimeoutPacket(ctx, packet, relayer)
}
89 changes: 43 additions & 46 deletions modules/apps/29-fee/keeper/escrow.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

"github.com/cosmos/ibc-go/modules/apps/29-fee/types"
channeltypes "github.com/cosmos/ibc-go/modules/core/04-channel/types"
)

// EscrowPacketFee sends the packet fee to the 29-fee module account to hold in escrow
Expand Down Expand Up @@ -43,68 +42,66 @@ func (k Keeper) EscrowPacketFee(ctx sdk.Context, identifiedFee *types.Identified
return nil
}

// DistributeFee pays the acknowledgement fee & receive fee for a given packetId while refunding the timeout fee to the refund account associated with the Fee
func (k Keeper) DistributeFee(ctx sdk.Context, refundAcc, forwardRelayer, reverseRelayer sdk.AccAddress, packetID *channeltypes.PacketId) error {
// check if there is a Fee in escrow for the given packetId
feeInEscrow, found := k.GetFeeInEscrow(ctx, packetID)
if !found {
return sdkerrors.Wrapf(types.ErrFeeNotFound, "with channelID %s, sequence %d", packetID.ChannelId, packetID.Sequence)
// DistributePacketFees pays the acknowledgement fee & receive fee for a given packetId while refunding the timeout fee to the refund account associated with the Fee.
func (k Keeper) DistributePacketFees(ctx sdk.Context, refundAcc, forwardRelayer string, reverseRelayer sdk.AccAddress, feeInEscrow types.IdentifiedPacketFee) {
// distribute fee for forward relaying
forward, err := sdk.AccAddressFromBech32(forwardRelayer)
if err == nil {
k.distributeFee(ctx, forward, feeInEscrow.Fee.ReceiveFee)
}

// send receive fee to forward relayer
err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, forwardRelayer, feeInEscrow.Fee.ReceiveFee)
if err != nil {
return sdkerrors.Wrap(err, "failed to send fee to forward relayer")
}
// distribute fee for reverse relaying
k.distributeFee(ctx, reverseRelayer, feeInEscrow.Fee.AckFee)

// send ack fee to reverse relayer
err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, reverseRelayer, feeInEscrow.Fee.AckFee)
// refund timeout fee refund,
refundAddr, err := sdk.AccAddressFromBech32(refundAcc)
if err != nil {
return sdkerrors.Wrap(err, "error sending fee to reverse relayer")
panic(fmt.Sprintf("could not parse refundAcc %s to sdk.AccAddress", refundAcc))
}

// refund timeout fee to refundAddr
err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, refundAcc, feeInEscrow.Fee.TimeoutFee)
if err != nil {
return sdkerrors.Wrap(err, "error refunding timeout fee")
}
// refund timeout fee for unused timeout
k.distributeFee(ctx, refundAddr, feeInEscrow.Fee.TimeoutFee)

// removes the fee from the store as fee is now paid
k.DeleteFeeInEscrow(ctx, packetID)

return nil
k.DeleteFeeInEscrow(ctx, feeInEscrow.PacketId)
}

// DistributeFeeTimeout pays the timeout fee for a given packetId while refunding the acknowledgement fee & receive fee to the refund account associated with the Fee
func (k Keeper) DistributeFeeTimeout(ctx sdk.Context, refundAcc, timeoutRelayer sdk.AccAddress, packetID *channeltypes.PacketId) error {
// check if there is a Fee in escrow for the given packetId
feeInEscrow, found := k.GetFeeInEscrow(ctx, packetID)
if !found {
return sdkerrors.Wrapf(types.ErrFeeNotFound, "for packetID %s", packetID)
}

// refund the receive fee
err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, refundAcc, feeInEscrow.Fee.ReceiveFee)
// DistributePacketsFeesTimeout pays the timeout fee for a given packetId while refunding the acknowledgement fee & receive fee to the refund account associated with the Fee
func (k Keeper) DistributePacketFeesOnTimeout(ctx sdk.Context, refundAcc string, timeoutRelayer sdk.AccAddress, feeInEscrow types.IdentifiedPacketFee) {
// check if refundAcc address works
refundAddr, err := sdk.AccAddressFromBech32(refundAcc)
if err != nil {
return sdkerrors.Wrap(err, "error refunding receive fee")
panic(fmt.Sprintf("could not parse refundAcc %s to sdk.AccAddress", refundAcc))
}

// refund the ack fee
err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, refundAcc, feeInEscrow.Fee.AckFee)
if err != nil {
return sdkerrors.Wrap(err, "error refunding ack fee")
}
// refund receive fee for unused forward relaying
k.distributeFee(ctx, refundAddr, feeInEscrow.Fee.ReceiveFee)

// pay the timeout fee to the timeout relayer
err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, timeoutRelayer, feeInEscrow.Fee.TimeoutFee)
if err != nil {
return sdkerrors.Wrap(err, "error sending fee to timeout relayer")
}
// refund ack fee for unused reverse relaying
k.distributeFee(ctx, refundAddr, feeInEscrow.Fee.AckFee)

// distribute fee for timeout relaying
k.distributeFee(ctx, timeoutRelayer, feeInEscrow.Fee.TimeoutFee)

// removes the fee from the store as fee is now paid
k.DeleteFeeInEscrow(ctx, packetID)
k.DeleteFeeInEscrow(ctx, feeInEscrow.PacketId)
}

return nil
// distributeFee will attempt to distribute the escrowed fee to the receiver address.
// If the distribution fails for any reason (such as the receiving address being blocked),
// the state changes will be discarded.
func (k Keeper) distributeFee(ctx sdk.Context, receiver sdk.AccAddress, fee sdk.Coins) {
// cache context before trying to send to reverse relayer
cacheCtx, writeFn := ctx.CacheContext()

err := k.bankKeeper.SendCoinsFromModuleToAccount(cacheCtx, types.ModuleName, receiver, fee)
if err == nil {
// write the cache
writeFn()

// NOTE: The context returned by CacheContext() refers to a new EventManager, so it needs to explicitly set events to the original context.
ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events())
}
}

func (k Keeper) RefundFeesOnChannel(ctx sdk.Context, portID, channelID string) error {
Expand Down
Loading