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

Don't pay a duplicate BOLT 12 invoice if ChannelManager is stale #3313

Merged

Conversation

valentinewallace
Copy link
Contributor

This fixes the following bug:
- An outbound payment is AwaitingInvoice
- We receive an invoice and lock the HTLCs into the relevant ChannelMonitors
- The monitors are successfully persisted, but the ChannelManager fails to
  persist, so the outbound payment remains AwaitingInvoice
- We restart, causing the channel to close due to a stale ChannelManager
- We receive a duplicate invoice, and attempt to pay it again due to the
  payment still being AwaitingInvoice in the stale ChannelManager

After the fix for this, we will notice that the payment is already locked into
the monitor on startup and transition the incorrectly-AwaitingInvoice payment
to Retryable, which prevents double-paying on duplicate invoice receipt.

Caught by @TheBlueMatt: #3140 (comment).

@jkczyz jkczyz self-requested a review September 12, 2024 19:41
lightning/src/ln/outbound_payment.rs Outdated Show resolved Hide resolved
lightning/src/ln/offers_tests.rs Outdated Show resolved Hide resolved
Copy link

codecov bot commented Sep 12, 2024

Codecov Report

Attention: Patch coverage is 95.09804% with 5 lines in your changes missing coverage. Please review.

Project coverage is 89.62%. Comparing base (6662c5c) to head (fbb3ab2).
Report is 12 commits behind head on main.

Files with missing lines Patch % Lines
lightning/src/ln/outbound_payment.rs 78.94% 2 Missing and 2 partials ⚠️
lightning/src/ln/offers_tests.rs 98.75% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3313      +/-   ##
==========================================
- Coverage   89.65%   89.62%   -0.03%     
==========================================
  Files         126      126              
  Lines      102546   102622      +76     
  Branches   102546   102622      +76     
==========================================
+ Hits        91935    91975      +40     
- Misses       7890     7924      +34     
- Partials     2721     2723       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@jkczyz jkczyz left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

Thanks, yea, I think this looks right, though it'd be nice to include a test that sent the payment MPP.

@valentinewallace
Copy link
Contributor Author

Thanks, yea, I think this looks right, though it'd be nice to include a test that sent the payment MPP.

Adapted the test to use MPP, as a fixup in case you meant a separate test.

Move the code that ensures that HTLCs locked into ChannelMonitors are
synchronized with the ChannelManager's OutboundPayments store to the
outbound_payments module.

This is useful both because ChannelManager::read is very long/confusing method,
so it's nice to encapsulate some of its functionality, and because we need to
fix an existing bug in this logic where we may risk double-paying an offer due
to outbound_payments being stale on startup. See the next commit for this
bugfix.
@valentinewallace
Copy link
Contributor Author

Rebased due to silent conflict.

@TheBlueMatt
Copy link
Collaborator

LGTM, feel free to squash IMO.

@valentinewallace
Copy link
Contributor Author

Squashed.

Copy link
Contributor

@jkczyz jkczyz left a comment

Choose a reason for hiding this comment

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

LGTM modulo any CI failures. Can't tell from the UI.

This fixes the following bug:
- An outbound payment is AwaitingInvoice
- We receive an invoice and lock the HTLCs into the relevant ChannelMonitors
- The monitors are successfully persisted, but the ChannelManager fails to
  persist, so the outbound payment remains AwaitingInvoice
- We restart, causing the channels to close due to a stale ChannelManager
- We receive a duplicate invoice, and attempt to pay it again due to the
  payment still being AwaitingInvoice in the stale ChannelManager

After the fix for this, we will notice that the payment is already locked into
the monitor on startup and transition the incorrectly-AwaitingInvoice payment
to Retryable, which prevents double-paying on duplicate invoice receipt.
@valentinewallace
Copy link
Contributor Author

Kicked CI 🤞

Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

LGTM but the test needs to make sure we actually get PaymentSent events, which is the other half of not double-paying, but can happen in a followup.

// attempt would have resulted in a PaymentFailed event here, since the only channels between
// Alice and Bob is closed. Since no 2nd attempt should be made, check that no events are
// generated in response to the duplicate invoice.
assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should follow through, claiming the HTLCs on-chain and making sure we get a PaymentSent event.

@TheBlueMatt TheBlueMatt merged commit 866cedf into lightningdevkit:main Sep 17, 2024
17 of 19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants