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

channeldb: index payments by payment addr, use payment hash as fallback #4285

Merged
merged 8 commits into from
May 28, 2020

Conversation

cfromknecht
Copy link
Contributor

@cfromknecht cfromknecht commented May 13, 2020

This PR adds a new persistent index on invoices to allow efficient
lookups and updates by payment address. This is stepping stone
to enable AMP payments where the payment hash will not be
associated with the target invoice.

Since 0.9, all new invoices include a random payment address,
however older invoices do not have this value. New invoices will
be added to the payment address index on a rolling basis, but
the payment hash index will continue to serve as a fallback in case
the target invoice was created prior to this commit. As such,
no migration is needed for invoices created prior to this commit,
but we include one to bump the db version number and initialize
the top-level index bucket.

This also unblocks #4167, since it allows us to insert a random
payment address for experimental keysend payments. In the future,
where keysend is rolled into the final AMP proposal, this ensures that
all keysend (experimental or otherwise) have a payment address
associated with the invoice. This is desired since we will be using
payment addresses entirely to identify and update AMP payments, so
we ensure the code paths between AMP and legacy keysend are
identical.

Further off, once payment addresses are required, we can stop inserting
invoices into the payment hash index entirely. Currently however, we rely
on invoices being indexed to support other operations, e.g. hodl settle/cancel
or legacy senders that don't understand MPP.

@cfromknecht cfromknecht added this to the 0.11.0 milestone May 13, 2020
@cfromknecht cfromknecht added database Related to the database/storage of LND migration payments Related to invoices/payments labels May 13, 2020
@joostjager
Copy link
Contributor

One thought that came up: (hodl) keysend payments and AMP payments can be settled by payment address. But how does that work for recurring payments to the same address? How to keep those apart when you want to settle or cancel? I think the set_id is needed? But does that also require another index?

@cfromknecht cfromknecht force-pushed the pay-addr-index branch 2 times, most recently from 58ffb70 to 46ebc79 Compare May 14, 2020 07:18
@@ -538,7 +553,9 @@ func (d *DB) InvoicesAddedSince(sinceAddIndex uint64) ([]Invoice, error) {
// full invoice is returned. Before setting the incoming HTLC, the values
// SHOULD be checked to ensure the payer meets the agreed upon contractual
// terms of the payment.
func (d *DB) LookupInvoice(paymentHash [32]byte) (Invoice, error) {
func (d *DB) LookupInvoice(paymentHash [32]byte,
payAddr *[32]byte) (Invoice, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a use case for specifying both?

Copy link
Contributor

Choose a reason for hiding this comment

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

Or if both are specified, is a sanity check useful to see if hash is always present and pointing to the same invoice if addr is present too?

Copy link
Contributor Author

@cfromknecht cfromknecht May 18, 2020

Choose a reason for hiding this comment

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

the reason to use both is so that we don't have to add a migration for existing invoices. with AMP the hash won't align with any particular invoice, but this wil be worked around by providing a nil hash in the InvoiceRef.

if we do a full migration we'd end up supporting either (hash, nil) or (nil, pay_addr). if we don't, then we need to support (hash, nil), (nil, pay_addr), or (hash, pay_addr), where the latter has a sanity check that they point to the same invoice.

atm, there isn't really a business use case for doing the full migration, it would only serve to clean up this bit of code. this may change in the in the future tho (if we begin exposing invoice lookups by payment addr and we want to expose these slightly older invoices), but it is still possible to do this migration retroactively when that time comes. it is not terribly difficult, but it is time consuming in the critical path

Copy link
Contributor Author

Choose a reason for hiding this comment

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

InvoiceRef added, supporting (hash, nil) and (hash, pay_addr). the final variant will be added once the related AMP changes go in

@cfromknecht cfromknecht force-pushed the pay-addr-index branch 4 times, most recently from 2c6e0b8 to 18d887a Compare May 20, 2020 22:34
@cfromknecht cfromknecht force-pushed the pay-addr-index branch 4 times, most recently from 02fc031 to d4eb6f3 Compare May 21, 2020 23:12
Copy link
Contributor

@joostjager joostjager left a comment

Choose a reason for hiding this comment

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

Great improvement with InvoiceRef!

err = tx.DeleteTopLevelBucket(graphMetaBucket)
if err != nil && err != kvdb.ErrBucketNotFound {
return err
for _, tlb := range topLevelBuckets {
Copy link
Contributor

Choose a reason for hiding this comment

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

If we're removing everything, can't it be a for each on the tx?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's less atomic, but also only used for testing afaia, what would we gain?

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm so it should really be moved to a _test.go file then.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah it's something left over from super early development, when the easiest thing to do was just delete the entire database and start over lol.

// hash, but pre 0.8 invoices do not have one at all. When this value is
// known it will be used as the primary identifier, falling back to
// payHash if no value is known.
payAddr *[32]byte
Copy link
Contributor

Choose a reason for hiding this comment

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

Not necessarily for this PR, but I can see an advantage of making an lntypes.Hash style datatype for payment address, especially if we are going to use it a lot

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes i've been thinking maybe we should, but held off because we don't use it for anything other than comparing equality. definitely something we can look into tho, it would be nice if they were unified

err = tx.DeleteTopLevelBucket(graphMetaBucket)
if err != nil && err != kvdb.ErrBucketNotFound {
return err
for _, tlb := range topLevelBuckets {
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm so it should really be moved to a _test.go file then.

@Roasbeef
Copy link
Member

Test failure on travis:

--- FAIL: TestChannelLinkBatchPreimageWrite (7.15s)
    --- FAIL: TestChannelLinkBatchPreimageWrite/flush_on_disconnect (6.20s)
        link_test.go:4746: unable to find preimages: expected to find witness: true, got false for hash=7888bd181c582842696baa9d3a55f8e324637da1c91dc89690aa71e3a04c91d0

Not sure if related.

@Roasbeef
Copy link
Member

New trigger in the error log filtration for the itests:

PASS
ok  	github.com/lightningnetwork/lnd/lntest/itest	990.562s
lntest/itest/log_check_errors.sh
<time> [ERR] NTFN: unable to get hash from block with height 790

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 🗿

Merge pending resolution of those two build issues I mentioned (log one is easy, other one may just be something else entirely, and thus out of scope).

@@ -142,6 +142,32 @@ const (
amtPaidType tlv.Type = 13
)

// InvoiceRef is an identifier for invoices supporting queries by payment hash.
Copy link
Member

Choose a reason for hiding this comment

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

me gusta

This line was incorrectly moved when the migtest package was created for
migration 12. This PR introduces a negative test for CreateTLB which
surfaced this.
This commit creates a generic migration for creating top-level buckets.
Neutrino can get slow towards the end of the itests.
@cfromknecht cfromknecht merged commit 0f3ab77 into lightningnetwork:master May 28, 2020
@githorray
Copy link
Contributor

Getting an error after this merge when trying to build lnd.

$ make install tags="experimental signrpc walletrpc chainrpc invoicesrpc"

github.com/lightningnetwork/lnd/channeldb
# github.com/lightningnetwork/lnd/channeldb
channeldb/invoices.go:657:54: undefined: kvdb.ReadBucket
Makefile:115: recipe for target 'build' failed
make: *** [build] Error 2```

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 migration payments Related to invoices/payments
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants