Skip to content

Commit d50f7ba

Browse files
authored
refactor: use branch service in 29-fee (#7732)
* refactor: use branch service in 29-fee * chore: move comment for readability * refactor: use branch service in timeout fee distribution * refactor: complete refactor to branch service and remove cached context * chore: make lint-fix
1 parent 661277c commit d50f7ba

File tree

2 files changed

+105
-100
lines changed

2 files changed

+105
-100
lines changed

modules/apps/29-fee/keeper/escrow.go

Lines changed: 104 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package keeper
33
import (
44
"bytes"
55
"context"
6+
"errors"
67
"fmt"
78

89
errorsmod "cosmossdk.io/errors"
@@ -11,6 +12,7 @@ import (
1112

1213
"github.com/cosmos/ibc-go/v9/modules/apps/29-fee/types"
1314
channeltypes "github.com/cosmos/ibc-go/v9/modules/core/04-channel/types"
15+
ibcerrors "github.com/cosmos/ibc-go/v9/modules/core/errors"
1416
)
1517

1618
// escrowPacketFee sends the packet fee to the 29-fee module account to hold in escrow
@@ -46,38 +48,39 @@ func (k Keeper) escrowPacketFee(ctx context.Context, packetID channeltypes.Packe
4648

4749
// DistributePacketFeesOnAcknowledgement pays all the acknowledgement & receive fees for a given packetID while refunding the timeout fees to the refund account.
4850
func (k Keeper) DistributePacketFeesOnAcknowledgement(ctx context.Context, forwardRelayer string, reverseRelayer sdk.AccAddress, packetFees []types.PacketFee, packetID channeltypes.PacketId) {
49-
// cache context before trying to distribute fees
51+
// use branched multistore for distribution of fees.
5052
// if the escrow account has insufficient balance then we want to avoid partially distributing fees
51-
sdkCtx := sdk.UnwrapSDKContext(ctx) // TODO: https://github.com/cosmos/ibc-go/issues/5917
52-
cacheCtx, writeFn := sdkCtx.CacheContext()
53+
if err := k.BranchService.Execute(ctx, func(ctx context.Context) error {
54+
// forward relayer address will be empty if conversion fails
55+
forwardAddr, _ := sdk.AccAddressFromBech32(forwardRelayer)
56+
57+
for _, packetFee := range packetFees {
58+
if !k.EscrowAccountHasBalance(ctx, packetFee.Fee.Total()) {
59+
// NOTE: we lock the fee module on error return so that the state changes are persisted
60+
return ibcerrors.ErrInsufficientFunds
61+
}
62+
63+
// check if refundAcc address works
64+
refundAddr, err := sdk.AccAddressFromBech32(packetFee.RefundAddress)
65+
if err != nil {
66+
panic(fmt.Errorf("could not parse refundAcc %s to sdk.AccAddress", packetFee.RefundAddress))
67+
}
5368

54-
// forward relayer address will be empty if conversion fails
55-
forwardAddr, _ := sdk.AccAddressFromBech32(forwardRelayer)
69+
k.distributePacketFeeOnAcknowledgement(ctx, refundAddr, forwardAddr, reverseRelayer, packetFee)
70+
}
5671

57-
for _, packetFee := range packetFees {
58-
if !k.EscrowAccountHasBalance(cacheCtx, packetFee.Fee.Total()) {
72+
return nil
73+
}); err != nil {
74+
if errors.Is(err, ibcerrors.ErrInsufficientFunds) {
5975
// if the escrow account does not have sufficient funds then there must exist a severe bug
6076
// the fee module should be locked until manual intervention fixes the issue
6177
// a locked fee module will simply skip fee logic, all channels will temporarily function as
6278
// fee disabled channels
63-
// NOTE: we use the uncached context to lock the fee module so that the state changes from
64-
// locking the fee module are persisted
6579
k.lockFeeModule(ctx)
6680
return
6781
}
68-
69-
// check if refundAcc address works
70-
refundAddr, err := sdk.AccAddressFromBech32(packetFee.RefundAddress)
71-
if err != nil {
72-
panic(fmt.Errorf("could not parse refundAcc %s to sdk.AccAddress", packetFee.RefundAddress))
73-
}
74-
75-
k.distributePacketFeeOnAcknowledgement(cacheCtx, refundAddr, forwardAddr, reverseRelayer, packetFee)
7682
}
7783

78-
// write the cache
79-
writeFn()
80-
8184
// removes the fees from the store as fees are now paid
8285
k.DeleteFeesInEscrow(ctx, packetID)
8386
}
@@ -104,35 +107,36 @@ func (k Keeper) distributePacketFeeOnAcknowledgement(ctx context.Context, refund
104107

105108
// DistributePacketFeesOnTimeout pays all the timeout fees for a given packetID while refunding the acknowledgement & receive fees to the refund account.
106109
func (k Keeper) DistributePacketFeesOnTimeout(ctx context.Context, timeoutRelayer sdk.AccAddress, packetFees []types.PacketFee, packetID channeltypes.PacketId) {
107-
// cache context before trying to distribute fees
110+
// use branched multistore for distribution of fees.
108111
// if the escrow account has insufficient balance then we want to avoid partially distributing fees
109-
sdkCtx := sdk.UnwrapSDKContext(ctx) // TODO: https://github.com/cosmos/ibc-go/issues/5917
110-
cacheCtx, writeFn := sdkCtx.CacheContext()
112+
if err := k.BranchService.Execute(ctx, func(ctx context.Context) error {
113+
for _, packetFee := range packetFees {
114+
if !k.EscrowAccountHasBalance(ctx, packetFee.Fee.Total()) {
115+
// NOTE: we lock the fee module on error return so that the state changes are persisted
116+
return ibcerrors.ErrInsufficientFunds
117+
}
111118

112-
for _, packetFee := range packetFees {
113-
if !k.EscrowAccountHasBalance(cacheCtx, packetFee.Fee.Total()) {
119+
// check if refundAcc address works
120+
refundAddr, err := sdk.AccAddressFromBech32(packetFee.RefundAddress)
121+
if err != nil {
122+
panic(fmt.Errorf("could not parse refundAcc %s to sdk.AccAddress", packetFee.RefundAddress))
123+
}
124+
125+
k.distributePacketFeeOnTimeout(ctx, refundAddr, timeoutRelayer, packetFee)
126+
}
127+
128+
return nil
129+
}); err != nil {
130+
if errors.Is(err, ibcerrors.ErrInsufficientFunds) {
114131
// if the escrow account does not have sufficient funds then there must exist a severe bug
115132
// the fee module should be locked until manual intervention fixes the issue
116133
// a locked fee module will simply skip fee logic, all channels will temporarily function as
117134
// fee disabled channels
118-
// NOTE: we use the uncached context to lock the fee module so that the state changes from
119-
// locking the fee module are persisted
120135
k.lockFeeModule(ctx)
121136
return
122137
}
123-
124-
// check if refundAcc address works
125-
refundAddr, err := sdk.AccAddressFromBech32(packetFee.RefundAddress)
126-
if err != nil {
127-
panic(fmt.Errorf("could not parse refundAcc %s to sdk.AccAddress", packetFee.RefundAddress))
128-
}
129-
130-
k.distributePacketFeeOnTimeout(cacheCtx, refundAddr, timeoutRelayer, packetFee)
131138
}
132139

133-
// write the cache
134-
writeFn()
135-
136140
// removing the fee from the store as the fee is now paid
137141
k.DeleteFeesInEscrow(ctx, packetID)
138142
}
@@ -151,36 +155,36 @@ func (k Keeper) distributePacketFeeOnTimeout(ctx context.Context, refundAddr, ti
151155
// If the distribution fails for any reason (such as the receiving address being blocked),
152156
// the state changes will be discarded.
153157
func (k Keeper) distributeFee(ctx context.Context, receiver, refundAccAddress sdk.AccAddress, fee sdk.Coins) {
154-
// cache context before trying to distribute fees
155-
sdkCtx := sdk.UnwrapSDKContext(ctx) // TODO: https://github.com/cosmos/ibc-go/issues/7223
156-
cacheCtx, writeFn := sdkCtx.CacheContext()
158+
// use branched multistore before trying to distribute fees
159+
if err := k.BranchService.Execute(ctx, func(ctx context.Context) error {
160+
err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, receiver, fee)
161+
if err != nil {
162+
if bytes.Equal(receiver, refundAccAddress) {
163+
// if sending to the refund address already failed, then return (no-op)
164+
return errorsmod.Wrapf(types.ErrRefundDistributionFailed, "receiver address: %s", receiver)
165+
}
157166

158-
err := k.bankKeeper.SendCoinsFromModuleToAccount(cacheCtx, types.ModuleName, receiver, fee)
159-
if err != nil {
160-
if bytes.Equal(receiver, refundAccAddress) {
161-
k.Logger.Error("error distributing fee", "receiver address", receiver, "fee", fee)
162-
return // if sending to the refund address already failed, then return (no-op)
163-
}
167+
// if an error is returned from x/bank and the receiver is not the refundAccAddress
168+
// then attempt to refund the fee to the original sender
169+
err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, refundAccAddress, fee)
170+
if err != nil {
171+
// if sending to the refund address fails, no-op
172+
return errorsmod.Wrapf(types.ErrRefundDistributionFailed, "receiver address: %s", refundAccAddress)
173+
}
164174

165-
// if an error is returned from x/bank and the receiver is not the refundAccAddress
166-
// then attempt to refund the fee to the original sender
167-
err := k.bankKeeper.SendCoinsFromModuleToAccount(cacheCtx, types.ModuleName, refundAccAddress, fee)
168-
if err != nil {
169-
k.Logger.Error("error refunding fee to the original sender", "refund address", refundAccAddress, "fee", fee)
170-
return // if sending to the refund address fails, no-op
175+
if err := k.emitDistributeFeeEvent(ctx, refundAccAddress.String(), fee); err != nil {
176+
panic(err)
177+
}
171178
}
172179

173-
if err := k.emitDistributeFeeEvent(ctx, refundAccAddress.String(), fee); err != nil {
174-
panic(err)
175-
}
176-
} else {
177-
if err := k.emitDistributeFeeEvent(ctx, receiver.String(), fee); err != nil {
178-
panic(err)
179-
}
180+
return nil
181+
}); err != nil {
182+
k.Logger.Error("error distributing fee", "error", err.Error())
180183
}
181184

182-
// write the cache
183-
writeFn()
185+
if err := k.emitDistributeFeeEvent(ctx, receiver.String(), fee); err != nil {
186+
panic(err)
187+
}
184188
}
185189

186190
// RefundFeesOnChannelClosure will refund all fees associated with the given port and channel identifiers.
@@ -190,52 +194,52 @@ func (k Keeper) distributeFee(ctx context.Context, receiver, refundAccAddress sd
190194
func (k Keeper) RefundFeesOnChannelClosure(ctx context.Context, portID, channelID string) error {
191195
identifiedPacketFees := k.GetIdentifiedPacketFeesForChannel(ctx, portID, channelID)
192196

193-
// cache context before trying to distribute fees
197+
// use branched multistore for distribution of fees.
194198
// if the escrow account has insufficient balance then we want to avoid partially distributing fees
195-
sdkCtx := sdk.UnwrapSDKContext(ctx) // TODO: https://github.com/cosmos/ibc-go/issues/5917
196-
cacheCtx, writeFn := sdkCtx.CacheContext()
197-
198-
for _, identifiedPacketFee := range identifiedPacketFees {
199-
var unRefundedFees []types.PacketFee
200-
for _, packetFee := range identifiedPacketFee.PacketFees {
201-
202-
if !k.EscrowAccountHasBalance(cacheCtx, packetFee.Fee.Total()) {
203-
// if the escrow account does not have sufficient funds then there must exist a severe bug
204-
// the fee module should be locked until manual intervention fixes the issue
205-
// a locked fee module will simply skip fee logic, all channels will temporarily function as
206-
// fee disabled channels
207-
// NOTE: we use the uncached context to lock the fee module so that the state changes from
208-
// locking the fee module are persisted
209-
k.lockFeeModule(ctx)
210-
211-
// return a nil error so state changes are committed but distribution stops
212-
return nil
199+
if err := k.BranchService.Execute(ctx, func(ctx context.Context) error {
200+
for _, identifiedPacketFee := range identifiedPacketFees {
201+
var unRefundedFees []types.PacketFee
202+
for _, packetFee := range identifiedPacketFee.PacketFees {
203+
204+
if !k.EscrowAccountHasBalance(ctx, packetFee.Fee.Total()) {
205+
// NOTE: we lock the fee module on error return so that the state changes are persisted
206+
return ibcerrors.ErrInsufficientFunds
207+
}
208+
209+
refundAddr, err := sdk.AccAddressFromBech32(packetFee.RefundAddress)
210+
if err != nil {
211+
unRefundedFees = append(unRefundedFees, packetFee)
212+
continue
213+
}
214+
215+
// refund all fees to refund address
216+
if err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, refundAddr, packetFee.Fee.Total()); err != nil {
217+
unRefundedFees = append(unRefundedFees, packetFee)
218+
continue
219+
}
213220
}
214221

215-
refundAddr, err := sdk.AccAddressFromBech32(packetFee.RefundAddress)
216-
if err != nil {
217-
unRefundedFees = append(unRefundedFees, packetFee)
218-
continue
219-
}
220-
221-
// refund all fees to refund address
222-
if err = k.bankKeeper.SendCoinsFromModuleToAccount(cacheCtx, types.ModuleName, refundAddr, packetFee.Fee.Total()); err != nil {
223-
unRefundedFees = append(unRefundedFees, packetFee)
224-
continue
222+
if len(unRefundedFees) > 0 {
223+
// update packet fees to keep only the unrefunded fees
224+
packetFees := types.NewPacketFees(unRefundedFees)
225+
k.SetFeesInEscrow(ctx, identifiedPacketFee.PacketId, packetFees)
226+
} else {
227+
k.DeleteFeesInEscrow(ctx, identifiedPacketFee.PacketId)
225228
}
226229
}
227230

228-
if len(unRefundedFees) > 0 {
229-
// update packet fees to keep only the unrefunded fees
230-
packetFees := types.NewPacketFees(unRefundedFees)
231-
k.SetFeesInEscrow(cacheCtx, identifiedPacketFee.PacketId, packetFees)
232-
} else {
233-
k.DeleteFeesInEscrow(cacheCtx, identifiedPacketFee.PacketId)
231+
return nil
232+
}); err != nil {
233+
if errors.Is(err, ibcerrors.ErrInsufficientFunds) {
234+
// if the escrow account does not have sufficient funds then there must exist a severe bug
235+
// the fee module should be locked until manual intervention fixes the issue
236+
// a locked fee module will simply skip fee logic, all channels will temporarily function as
237+
// fee disabled channels
238+
k.lockFeeModule(ctx)
239+
240+
return nil // commit state changes to lock module and stop fee distribution
234241
}
235242
}
236243

237-
// write the cache
238-
writeFn()
239-
240244
return nil
241245
}

modules/apps/29-fee/types/errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ var (
1717
ErrRelayerNotFoundForAsyncAck = errorsmod.Register(ModuleName, 10, "relayer address must be stored for async WriteAcknowledgement")
1818
ErrFeeModuleLocked = errorsmod.Register(ModuleName, 11, "the fee module is currently locked, a severe bug has been detected")
1919
ErrUnsupportedAction = errorsmod.Register(ModuleName, 12, "unsupported action")
20+
ErrRefundDistributionFailed = errorsmod.Register(ModuleName, 13, "refund distribution failed")
2021
)

0 commit comments

Comments
 (0)