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

chore(ics29): attempt to refund fees on distribution failure #1245

Merged
merged 7 commits into from
Apr 12, 2022

Conversation

damiannolan
Copy link
Member

Description

  • Attempt to refund fees on distribution failure

closes: #862


Before we can merge this PR, please make sure that all the following items have been
checked off. If any of the checklist items are not applicable, please leave them but
write a little note why.

  • Targeted PR against correct branch (see CONTRIBUTING.md)
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Code follows the module structure standards.
  • Wrote unit and integration tests
  • Updated relevant documentation (docs/) or specification (x/<module>/spec/)
  • Added relevant godoc comments.
  • Added a relevant changelog entry to the Unreleased section in CHANGELOG.md
  • Re-reviewed Files changed in the Github PR explorer
  • Review Codecov Report in the comment section below once CI passes

@damiannolan
Copy link
Member Author

Not sure if this requires more tests to be added.

if err != nil && !bytes.Equal(receiver, refundAccAddress) {
err := k.bankKeeper.SendCoinsFromModuleToAccount(cacheCtx, types.ModuleName, refundAccAddress, fee)
if err != nil {
return
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anything we can do here in the case of both failing? Emit some events?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be the goal for emitting the event? Events are mostly just used by relayers I guess.

I think it would be worth logging something to INFO or DEBUG logs. Although I haven't seen SDK modules utilizing logging much.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be the goal for emitting the event?

Just some kind of signalling I guess, was just thinking aloud really. I think logging at WARN or ERROR here may be useful, but like you say, most sdk modules don't seem to do much logging.
Feels a little wrong to just silently no-op, that's my only concern.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I know what you mean. I guess the problem is that even if we returned an error here we would simply dismiss it at the level above anyway, otherwise, we would err on OnAcknowledgementPacket which would throw away the ack for the ics20 transfer or w/e as well.

I think we're limited in what we can do here so my vote would be some warn/err logs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a bug if we cannot refund to the original sender? Maybe we should panic

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, maybe we should... originally @AdityaSripal had suggested to just no-op in the issue.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the no-op is for the case that the other fees were distributed fine but this one errors. If we err or panic the other fees won't ever get distributed.


// 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())
if err != nil && !bytes.Equal(receiver, refundAccAddress) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be worth adding a comment here explaining why this logic is necessary. I find it a bit confusing. 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to separate these conditions as well. What happens if there is an err and refundAcc == receiver?

Maybe something like:

if err != nil {
    // fee distribution failed, try to refund to original incentivizer
    if bytes.Equal(receiver, refundAccAddress) {
        return err
    }
    
    // attempt to distribute fee to refund address
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a test case for this case?

Copy link
Member Author

@damiannolan damiannolan Apr 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's an error and refundAcc == receiver then sending to the refundAcc has already failed, right?
That's why I combined the conditions using the logical AND.
The issues writes:

If we are trying to refund to original sender and that fails, then we need to just no-op and continue.

In this case, writeFn() is still called but there would be no state changes to commit (no-op). Do you think this would cause any problems?
I can adapt this tomorrow tho if we feel its better separated.

I'll look at adding a testcase 👍

Copy link
Member Author

@damiannolan damiannolan Apr 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests require some refactoring. In progress!

edit: oh man, just realised some of the same refactoring was merged yesterday in #1239

Copy link
Contributor

@colin-axner colin-axner Apr 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be on the safe side I think it makes sense to ensure writeFn() doesn't get called. Otherwise we need to verify that no partial state transitions occurred in x/bank. It might assume state changes are discarded once the error is returned. Nice catch on the refundAcc == receiver transfer already occurring.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I'll adapt the logic to represent that. I had similar concerns wrt writeFn() but wasn't 100%. Thanks :)

// check if the reverse relayer is paid
hasBalance := suite.chainA.GetSimApp().BankKeeper.HasBalance(suite.chainA.GetContext(), reverseRelayer, fee.AckFee[0].Add(fee.AckFee[0]))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should be using HasBalance as it just ensures it is greater than or equal to.

Updated to be explicit.
Fetch the account balances below, pre fee distribution, then assert the required amounts are added.

},
},
// {
// "invalid refund address: no-op, timeout fee remains in escrow",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll see if I can fix this test. Currently failing because we're relying on EscrowPacketFee and malleate() is called after this. If we call malleate() before escrowing the fees, then the RefundAddress (incentivizer) is the module account, which doesn't have balance.

Bit of a mouth full, but hope it makes sense.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The simplest solution is to mutate packetFee.RefundAddress in malleate() after the fees are initially escrowed and before DistributePacketFees is called 👍

Copy link
Contributor

@colin-axner colin-axner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! Thanks for following up on my suggested change

@damiannolan damiannolan enabled auto-merge (squash) April 12, 2022 15:16
@damiannolan damiannolan merged commit 960a49e into main Apr 12, 2022
@damiannolan damiannolan deleted the damian/862-fee-distribution-failure branch April 12, 2022 15:22
CosmosCar pushed a commit to caelus-labs/ibc-go that referenced this pull request Nov 6, 2023
cosmos#1245)

<!--
Please read and fill out this form before submitting your PR.

Please make sure you have reviewed our contributors guide before
submitting your
first PR.
-->

## Overview
Closes: cosmos#1246 
<!-- 
Please provide an explanation of the PR, including the appropriate
context,
background, goal, and rationale. If there is an issue with this
information,
please provide a tl;dr and link the issue. 
-->

## Checklist

<!-- 
Please complete the checklist to ensure that the PR is ready to be
reviewed.

IMPORTANT:
PRs should be left in Draft until the below checklist is completed.
-->

- [x] New and updated code has appropriate documentation
- [x] New and updated code has new and/or updated testing
- [x] Required CI checks are passing
- [ ] Visual proof for any user facing features like CLI or
documentation updates
- [x] Linked issues closed with keywords
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

If distribute fee fails, refund to original sender
3 participants