|
1 | 1 | package keeper |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "errors" |
5 | 4 | "fmt" |
6 | 5 | "strings" |
7 | 6 |
|
@@ -320,67 +319,46 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data t |
320 | 319 | // acknowledgement was a success then nothing occurs. If the acknowledgement failed, |
321 | 320 | // then the sender is refunded their tokens using the refundPacketToken function. |
322 | 321 | func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketDataV2, ack channeltypes.Acknowledgement) error { |
323 | | - prevPacket, found := k.GetForwardedPacket(ctx, packet.SourcePort, packet.SourceChannel, packet.Sequence) |
324 | | - if found { |
325 | | - channelCap, ok := k.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(packet.SourcePort, packet.SourceChannel)) |
326 | | - if !ok { |
327 | | - return errorsmod.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") |
328 | | - } |
| 322 | + prevPacket, isForwarded := k.GetForwardedPacket(ctx, packet.SourcePort, packet.SourceChannel, packet.Sequence) |
329 | 323 |
|
330 | | - switch ack.Response.(type) { |
331 | | - case *channeltypes.Acknowledgement_Result: |
332 | | - // the acknowledgement succeeded on the receiving chain so |
333 | | - // we write the asynchronous acknowledgement for the sender |
334 | | - // of the previous packet. |
335 | | - fungibleTokenPacketAcknowledgement := channeltypes.NewResultAcknowledgement([]byte("forwarded packet succeeded")) |
336 | | - return k.ics4Wrapper.WriteAcknowledgement(ctx, channelCap, prevPacket, fungibleTokenPacketAcknowledgement) |
337 | | - case *channeltypes.Acknowledgement_Error: |
338 | | - // the forwarded packet has failed, thus the funds have been refunded to the forwarding address. |
339 | | - // we must revert the changes that came from successfully receiving the tokens on our chain |
340 | | - // before propogating the error acknowledgement back to original sender chain |
341 | | - if err := k.revertInFlightChanges(ctx, packet, prevPacket, data); err != nil { |
342 | | - return err |
343 | | - } |
| 324 | + switch ack.Response.(type) { |
| 325 | + case *channeltypes.Acknowledgement_Result: |
| 326 | + if isForwarded { |
| 327 | + return k.ackForwardPacketSuccess(ctx, prevPacket) |
| 328 | + } |
344 | 329 |
|
345 | | - fungibleTokenPacketAcknowledgement := channeltypes.NewErrorAcknowledgement(errors.New("forwarded packet failed")) |
346 | | - return k.ics4Wrapper.WriteAcknowledgement(ctx, channelCap, prevPacket, fungibleTokenPacketAcknowledgement) |
347 | | - default: |
348 | | - return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected one of [%T, %T], got %T", channeltypes.Acknowledgement_Result{}, channeltypes.Acknowledgement_Error{}, ack.Response) |
| 330 | + // the acknowledgement succeeded on the receiving chain so nothing |
| 331 | + // needs to be executed and no error needs to be returned |
| 332 | + return nil |
| 333 | + case *channeltypes.Acknowledgement_Error: |
| 334 | + // We refund the tokens from the escrow address to the sender |
| 335 | + if err := k.refundPacketTokens(ctx, packet, data); err != nil { |
| 336 | + return err |
349 | 337 | } |
350 | | - } else { |
351 | | - switch ack.Response.(type) { |
352 | | - case *channeltypes.Acknowledgement_Result: |
353 | | - // the acknowledgement succeeded on the receiving chain so nothing |
354 | | - // needs to be executed and no error needs to be returned |
355 | | - return nil |
356 | | - case *channeltypes.Acknowledgement_Error: |
357 | | - return k.refundPacketTokens(ctx, packet, data) |
358 | | - default: |
359 | | - return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected one of [%T, %T], got %T", channeltypes.Acknowledgement_Result{}, channeltypes.Acknowledgement_Error{}, ack.Response) |
| 338 | + if isForwarded { |
| 339 | + return k.ackForwardPacketError(ctx, prevPacket, data) |
360 | 340 | } |
| 341 | + |
| 342 | + return nil |
| 343 | + default: |
| 344 | + return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected one of [%T, %T], got %T", channeltypes.Acknowledgement_Result{}, channeltypes.Acknowledgement_Error{}, ack.Response) |
361 | 345 | } |
362 | 346 | } |
363 | 347 |
|
364 | 348 | // OnTimeoutPacket either reverts the state changes executed in receive and send |
365 | 349 | // packet if the chain acted as a middle hop on a multihop transfer; or refunds |
366 | 350 | // the sender if the original packet sent was never received and has been timed out. |
367 | 351 | func (k Keeper) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketDataV2) error { |
368 | | - prevPacket, found := k.GetForwardedPacket(ctx, packet.SourcePort, packet.SourceChannel, packet.Sequence) |
369 | | - if found { |
370 | | - channelCap, ok := k.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(packet.SourcePort, packet.SourceChannel)) |
371 | | - if !ok { |
372 | | - return errorsmod.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") |
373 | | - } |
374 | | - |
375 | | - if err := k.revertInFlightChanges(ctx, packet, prevPacket, data); err != nil { |
376 | | - return err |
377 | | - } |
| 352 | + if err := k.refundPacketTokens(ctx, packet, data); err != nil { |
| 353 | + return err |
| 354 | + } |
378 | 355 |
|
379 | | - fungibleTokenPacketAcknowledgement := channeltypes.NewErrorAcknowledgement(fmt.Errorf("forwarded packet timed out")) |
380 | | - return k.ics4Wrapper.WriteAcknowledgement(ctx, channelCap, prevPacket, fungibleTokenPacketAcknowledgement) |
| 356 | + prevPacket, isForwarded := k.GetForwardedPacket(ctx, packet.SourcePort, packet.SourceChannel, packet.Sequence) |
| 357 | + if isForwarded { |
| 358 | + return k.ackForwardPacketTimeout(ctx, prevPacket, data) |
381 | 359 | } |
382 | 360 |
|
383 | | - return k.refundPacketTokens(ctx, packet, data) |
| 361 | + return nil |
384 | 362 | } |
385 | 363 |
|
386 | 364 | // refundPacketTokens will unescrow and send back the tokens back to sender |
@@ -429,63 +407,6 @@ func (k Keeper) refundPacketTokens(ctx sdk.Context, packet channeltypes.Packet, |
429 | 407 | return nil |
430 | 408 | } |
431 | 409 |
|
432 | | -// revertInFlightChanges reverts the logic of receive packet and send packet |
433 | | -// that occurs in the middle chains during a packet forwarding. If an error |
434 | | -// occurs further down the line, the state changes on this chain must be |
435 | | -// reverted before sending back the error acknowledgement to ensure atomic packet forwarding. |
436 | | -func (k Keeper) revertInFlightChanges(ctx sdk.Context, sentPacket channeltypes.Packet, receivedPacket channeltypes.Packet, sentPacketData types.FungibleTokenPacketDataV2) error { |
437 | | - forwardEscrow := types.GetEscrowAddress(sentPacket.SourcePort, sentPacket.SourceChannel) |
438 | | - reverseEscrow := types.GetEscrowAddress(receivedPacket.DestinationPort, receivedPacket.DestinationChannel) |
439 | | - |
440 | | - // the token on our chain is the token in the sentPacket |
441 | | - for _, token := range sentPacketData.Tokens { |
442 | | - // parse the transfer amount |
443 | | - transferAmount, ok := sdkmath.NewIntFromString(token.Amount) |
444 | | - if !ok { |
445 | | - return errorsmod.Wrapf(types.ErrInvalidAmount, "unable to parse transfer amount (%s) into math.Int", transferAmount) |
446 | | - } |
447 | | - coin := sdk.NewCoin(token.Denom.IBCDenom(), transferAmount) |
448 | | - |
449 | | - // check if the packet we sent out was sending as source or not |
450 | | - // if it is source, then we escrowed the outgoing tokens |
451 | | - if token.Denom.SenderChainIsSource(sentPacket.SourcePort, sentPacket.SourceChannel) { |
452 | | - // check if the packet we received was a source token for our chain |
453 | | - // check if here should be ReceiverChainIsSource |
454 | | - if token.Denom.SenderChainIsSource(receivedPacket.DestinationPort, receivedPacket.DestinationChannel) { |
455 | | - // receive sent tokens from the received escrow to the forward escrow account |
456 | | - // so we must send the tokens back from the forward escrow to the original received escrow account |
457 | | - return k.unescrowCoin(ctx, forwardEscrow, reverseEscrow, coin) |
458 | | - } |
459 | | - |
460 | | - // receive minted vouchers and sent to the forward escrow account |
461 | | - // so we must remove the vouchers from the forward escrow account and burn them |
462 | | - if err := k.bankKeeper.BurnCoins( |
463 | | - ctx, types.ModuleName, sdk.NewCoins(coin), |
464 | | - ); err != nil { |
465 | | - return err |
466 | | - } |
467 | | - } else { //nolint:gocritic |
468 | | - // in this case we burned the vouchers of the outgoing packets |
469 | | - // check if the packet we received was a source token for our chain |
470 | | - // in this case, the tokens were unescrowed from the reverse escrow account |
471 | | - if token.Denom.SenderChainIsSource(receivedPacket.DestinationPort, receivedPacket.DestinationChannel) { |
472 | | - // in this case we must mint the burned vouchers and send them back to the escrow account |
473 | | - if err := k.bankKeeper.MintCoins(ctx, types.ModuleName, sdk.NewCoins(coin)); err != nil { |
474 | | - return err |
475 | | - } |
476 | | - |
477 | | - if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, reverseEscrow, sdk.NewCoins(coin)); err != nil { |
478 | | - panic(fmt.Errorf("unable to send coins from module to account despite previously minting coins to module account: %v", err)) |
479 | | - } |
480 | | - } |
481 | | - |
482 | | - // if it wasn't a source token on receive, then we simply had minted vouchers and burned them in the receive. |
483 | | - // So no state changes were made, and thus no reversion is necessary |
484 | | - } |
485 | | - } |
486 | | - return nil |
487 | | -} |
488 | | - |
489 | 410 | // escrowCoin will send the given coin from the provided sender to the escrow address. It will also |
490 | 411 | // update the total escrowed amount by adding the escrowed coin's amount to the current total escrow. |
491 | 412 | func (k Keeper) escrowCoin(ctx sdk.Context, sender, escrowAddress sdk.AccAddress, coin sdk.Coin) error { |
@@ -572,3 +493,20 @@ func createPacketDataBytesFromVersion(appVersion, sender, receiver, memo string, |
572 | 493 |
|
573 | 494 | return packetDataBytes |
574 | 495 | } |
| 496 | + |
| 497 | +// burnCoin sends coins from the account to the transfer module account and then burn them. |
| 498 | +// We do this because bankKeeper.BurnCoins only works with a module account in SDK v0.50, |
| 499 | +// the next version of the SDK will allow burning coins from any account. |
| 500 | +// TODO: remove this function once we switch forwarding address to a module account (#6561) |
| 501 | +func (k Keeper) burnCoin(ctx sdk.Context, account sdk.AccAddress, coin sdk.Coin) error { |
| 502 | + coins := sdk.NewCoins(coin) |
| 503 | + if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, account, types.ModuleName, coins); err != nil { |
| 504 | + return err |
| 505 | + } |
| 506 | + |
| 507 | + if err := k.bankKeeper.BurnCoins(ctx, types.ModuleName, coins); err != nil { |
| 508 | + return err |
| 509 | + } |
| 510 | + |
| 511 | + return nil |
| 512 | +} |
0 commit comments