Skip to content

Conversation

@joostjager
Copy link
Contributor

@joostjager joostjager commented Aug 9, 2019

This PR modifies invoice registry so that it tracks the individual htlcs paying to an invoice. This leads to the following improvements:

  • Report the amount paid to an invoice correctly. Previously only the amount of the first HTLC was reported. With this PR, that is changed to the sum of all settled HTLCs.

  • Report channel through which an invoice was paid.

  • Consistent HTLC acceptance for hodl invoices. Previously the HTLC expiry check was skipped in certain situations which could lead to channel closure.

  • More generally, this PR adds the fine-grained HTLC control that is required for multi path payments.

NOTE: The migration of invoices in this pr cannot fill in the invoice htlcs for existing invoices. If there are invoices in the accepted state during the upgrade, the held htlcs may be released on startup. This risk can be eliminated by ensuring there are no invoices in the accepted state.

Fixes #3169

Fixes #2208

@joostjager joostjager force-pushed the invoice-circuits branch 3 times, most recently from 323e1a4 to 3bc173c Compare August 9, 2019 14:05
@joostjager joostjager force-pushed the invoice-circuits branch 4 times, most recently from 1f2610f to c3ada97 Compare August 14, 2019 19:29
@joostjager joostjager force-pushed the invoice-circuits branch 2 times, most recently from 085d358 to 152f4a3 Compare August 15, 2019 18:12
@joostjager joostjager requested a review from cfromknecht August 19, 2019 12:22
@joostjager joostjager force-pushed the invoice-circuits branch 2 times, most recently from e005e22 to 1267758 Compare August 19, 2019 13:28
@joostjager joostjager changed the title channeldb+cnct+invoices: track invoice htlcs [wip] channeldb+cnct+invoices: track invoice htlcs Aug 19, 2019
@wpaulino wpaulino added database Related to the database/storage of LND payments Related to invoices/payments v0.8.0 labels Aug 19, 2019
@wpaulino wpaulino added this to the 0.8.0 milestone Aug 19, 2019
@joostjager
Copy link
Contributor Author

While working on mpp and with amp in mind, I already saw the extra invoice htlc fields coming. Added a commit fixup! channeldb+invoices: add invoice htlcs that converts the serialization to a tlv stream. It is unfortunate that the format has changed twice, but probably still better than a new migration.

@joostjager joostjager force-pushed the invoice-circuits branch 3 times, most recently from 0d6bd4d to 445d9ff Compare September 3, 2019 12:18
@joostjager
Copy link
Contributor Author

Also modified old migration calls to serializeInvoice and deserializeInvoice in migrations.go to the legacy version.

Migrations that aren't fully self-contained are dangerous.

Copy link
Contributor

@cfromknecht cfromknecht left a comment

Choose a reason for hiding this comment

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

LGTM ✅ excited to start using tlv in our database, will make any future "migrations" to invoice htlcs straightforward :D

@cfromknecht
Copy link
Contributor

(still a stray fixup commit btw)

Previously a check was made for accepted and settled invoices against
the paid amount. This opens up a probe vector where an attacker can pay
to an invoice with an amt that is higher than the invoice amount and
find out if the invoice is already paid or not.
Add logic to specifically exercise the replay behavior of invoice
registry for hodl invoices.
Currently the invoice registry cannot tell apart the htlcs that pay to
an invoice. Because htlcs may also be replayed on startup, it isn't
possible to determine the total amount paid to an invoice.

This commit is a first step towards fixing that. It reports the circuit
keys of htlcs to the invoice registry, which forms the basis for
accurate invoice accounting.
This commit adds a set of htlcs to the Invoice struct and
serializes/deserializes this set to/from disk. It is a preparation for
accurate invoice accounting across restarts of lnd.

A migration is added for the invoice htlcs.

In addition to these changes, separate final cltv delta and expiry
invoice fields are created and populated. Previously it was required
to decode this from the stored payment request. The reason to create
a combined commit is to prevent multiple migrations.
Now that the Invoice struct contains the decoded final cltv delta value,
the decoding of payment requests can be removed from the invoice
registry.
As the logic around invoice mutations gets more complex, the friction
caused by having this logic split between invoice registry and channeldb
becomes more apparent. This commit brings a clearer separation of
concerns by centralizing the accept/settle logic in the invoice
registry.

The original AcceptOrSettle method is renamed to UpdateInvoice because
the update to perform is controlled by the callback.
This commit is a continuation of the centralization of invoice state
transition logic in the invoice registry.
This commit is a continuation of the centralization of invoice state
transition logic in the invoice registry.
This commit refactors the invoice registry accept/settle logic so that
it doesn't rely anymore on a set of error values to indirectly
communicate from the update callback to the main function what action is
required on the htlc.
Needed for time control in unit tests.
Previously the invoice registry wasn't aware of replayed htlcs. This was
dealt with by keeping the invoice accept/settle logic idempotent, so
that a replay wouldn't have an effect.

This mechanism has two limitations:

1. No accurate tracking of the total amount paid to an invoice. The total
amount couldn't just be increased with every htlc received, because it
could be a replay which would lead to counting the htlc amount multiple
times. Therefore the total amount was set to the amount of the first
htlc that was received, even though there may have been multiple htlcs
paying to the invoice.

2. Impossible to check htlc expiry consistently for hodl invoices. When
an htlc is new, its expiry needs to be checked against the invoice cltv
delta. But for a replay, that check must be skipped. The htlc was
accepted in time, the invoice was moved to the accepted state and a
replay some blocks later shouldn't lead to that htlc being cancelled.
Because the invoice registry couldn't recognize replays, it stopped
checking htlc expiry heights when the invoice reached the accepted
state. This prevents hold htlcs from being cancelled after a restart.
But unfortunately this also caused additional htlcs to be accepted on an
already accepted invoice without their expiry being checked.

In this commit, the invoice registry starts to persistently track htlcs
so that replays can be recognized. For replays, an htlc resolution
action is returned early. This fixes both limitations mentioned above.
@joostjager
Copy link
Contributor Author

fixup squashed

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

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

LGTM 🏭

@Roasbeef Roasbeef merged commit 7eca7b0 into lightningnetwork:master Sep 5, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

database Related to the database/storage of LND payments Related to invoices/payments

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ability to see which channel an invoice was paid through Invoices that are paid multiple times do not reflect their full amount paid

4 participants