diff --git a/channeldb/codec.go b/channeldb/codec.go index 8c39f4d7313..e5bab3d5f76 100644 --- a/channeldb/codec.go +++ b/channeldb/codec.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net" + "time" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" @@ -182,11 +183,6 @@ func WriteElement(w io.Writer, element interface{}) error { return err } - case paymentIndexType: - if err := binary.Write(w, byteOrder, e); err != nil { - return err - } - case lnwire.FundingFlag: if err := binary.Write(w, byteOrder, e); err != nil { return err @@ -415,11 +411,6 @@ func ReadElement(r io.Reader, element interface{}) error { return err } - case *paymentIndexType: - if err := binary.Read(r, byteOrder, e); err != nil { - return err - } - case *lnwire.FundingFlag: if err := binary.Read(r, byteOrder, e); err != nil { return err @@ -466,3 +457,37 @@ func ReadElements(r io.Reader, elements ...interface{}) error { } return nil } + +// deserializeTime deserializes time as unix nanoseconds. +func deserializeTime(r io.Reader) (time.Time, error) { + var scratch [8]byte + if _, err := io.ReadFull(r, scratch[:]); err != nil { + return time.Time{}, err + } + + // Convert to time.Time. Interpret unix nano time zero as a zero + // time.Time value. + unixNano := byteOrder.Uint64(scratch[:]) + if unixNano == 0 { + return time.Time{}, nil + } + + return time.Unix(0, int64(unixNano)), nil +} + +// serializeTime serializes time as unix nanoseconds. +func serializeTime(w io.Writer, t time.Time) error { + var scratch [8]byte + + // Convert to unix nano seconds, but only if time is non-zero. Calling + // UnixNano() on a zero time yields an undefined result. + var unixNano int64 + if !t.IsZero() { + unixNano = t.UnixNano() + } + + byteOrder.PutUint64(scratch[:], uint64(unixNano)) + _, err := w.Write(scratch[:]) + + return err +} diff --git a/channeldb/db.go b/channeldb/db.go index 715b906867a..00b29f65f9f 100644 --- a/channeldb/db.go +++ b/channeldb/db.go @@ -203,11 +203,13 @@ var ( migration: mig.CreateTLB(payAddrIndexBucket), }, { - // Initialize payment index bucket which will be used - // to index payments by sequence number. This index will - // be used to allow more efficient ListPayments queries. - number: 15, - migration: mig.CreateTLB(paymentsIndexBucket), + // This used to be create payment related top-level + // buckets, however this is now done by the payment + // package. + number: 15, + migration: func(tx kvdb.RwTx) error { + return nil + }, }, { // Add our existing payments to the index bucket created @@ -450,7 +452,6 @@ var dbTopLevelBuckets = [][]byte{ invoiceBucket, payAddrIndexBucket, setIDIndexBucket, - paymentsIndexBucket, peersBucket, nodeInfoBucket, metaBucket, diff --git a/channeldb/invoices.go b/channeldb/invoices.go index 161fa4a2ee3..ab8d1426fa6 100644 --- a/channeldb/invoices.go +++ b/channeldb/invoices.go @@ -553,7 +553,7 @@ func (d *DB) QueryInvoices(_ context.Context, q invpkg.InvoiceQuery) ( // Create a paginator which reads from our add index bucket with // the parameters provided by the invoice query. - paginator := newPaginator( + paginator := NewPaginator( invoiceAddIndex.ReadCursor(), q.Reversed, q.IndexOffset, q.NumMaxInvoices, ) @@ -603,7 +603,7 @@ func (d *DB) QueryInvoices(_ context.Context, q invpkg.InvoiceQuery) ( // Query our paginator using accumulateInvoices to build up a // set of invoices. - if err := paginator.query(accumulateInvoices); err != nil { + if err := paginator.Query(accumulateInvoices); err != nil { return err } diff --git a/channeldb/paginate.go b/channeldb/paginate.go index 496c236e051..dbb3548ebd4 100644 --- a/channeldb/paginate.go +++ b/channeldb/paginate.go @@ -16,9 +16,9 @@ type paginator struct { totalItems uint64 } -// newPaginator returns a struct which can be used to query an indexed bucket +// NewPaginator returns a struct which can be used to query an indexed bucket // in pages. -func newPaginator(c kvdb.RCursor, reversed bool, +func NewPaginator(c kvdb.RCursor, reversed bool, indexOffset, totalItems uint64) paginator { return paginator{ @@ -105,14 +105,14 @@ func (p paginator) cursorStart() ([]byte, []byte) { return indexKey, indexValue } -// query gets the start point for our index offset and iterates through keys +// Query gets the start point for our index offset and iterates through keys // in our index until we reach the total number of items required for the query // or we run out of cursor values. This function takes a fetchAndAppend function // which is responsible for looking up the entry at that index, adding the entry // to its set of return items (if desired) and return a boolean which indicates // whether the item was added. This is required to allow the paginator to // determine when the response has the maximum number of required items. -func (p paginator) query(fetchAndAppend func(k, v []byte) (bool, error)) error { +func (p paginator) Query(fetchAndAppend func(k, v []byte) (bool, error)) error { indexKey, indexValue := p.cursorStart() var totalItems int diff --git a/channeldb/payments.go b/channeldb/payments.go deleted file mode 100644 index a23f891d074..00000000000 --- a/channeldb/payments.go +++ /dev/null @@ -1,162 +0,0 @@ -package channeldb - -import ( - "fmt" - "time" - - "github.com/lightningnetwork/lnd/lntypes" - "github.com/lightningnetwork/lnd/lnwire" -) - -// FailureReason encodes the reason a payment ultimately failed. -type FailureReason byte - -const ( - // FailureReasonTimeout indicates that the payment did timeout before a - // successful payment attempt was made. - FailureReasonTimeout FailureReason = 0 - - // FailureReasonNoRoute indicates no successful route to the - // destination was found during path finding. - FailureReasonNoRoute FailureReason = 1 - - // FailureReasonError indicates that an unexpected error happened during - // payment. - FailureReasonError FailureReason = 2 - - // FailureReasonPaymentDetails indicates that either the hash is unknown - // or the final cltv delta or amount is incorrect. - FailureReasonPaymentDetails FailureReason = 3 - - // FailureReasonInsufficientBalance indicates that we didn't have enough - // balance to complete the payment. - FailureReasonInsufficientBalance FailureReason = 4 - - // FailureReasonCanceled indicates that the payment was canceled by the - // user. - FailureReasonCanceled FailureReason = 5 - - // TODO(joostjager): Add failure reasons for: - // LocalLiquidityInsufficient, RemoteCapacityInsufficient. -) - -// Error returns a human-readable error string for the FailureReason. -func (r FailureReason) Error() string { - return r.String() -} - -// String returns a human-readable FailureReason. -func (r FailureReason) String() string { - switch r { - case FailureReasonTimeout: - return "timeout" - case FailureReasonNoRoute: - return "no_route" - case FailureReasonError: - return "error" - case FailureReasonPaymentDetails: - return "incorrect_payment_details" - case FailureReasonInsufficientBalance: - return "insufficient_balance" - case FailureReasonCanceled: - return "canceled" - } - - return "unknown" -} - -// PaymentCreationInfo is the information necessary to have ready when -// initiating a payment, moving it into state InFlight. -type PaymentCreationInfo struct { - // PaymentIdentifier is the hash this payment is paying to in case of - // non-AMP payments, and the SetID for AMP payments. - PaymentIdentifier lntypes.Hash - - // Value is the amount we are paying. - Value lnwire.MilliSatoshi - - // CreationTime is the time when this payment was initiated. - CreationTime time.Time - - // PaymentRequest is the full payment request, if any. - PaymentRequest []byte - - // FirstHopCustomRecords are the TLV records that are to be sent to the - // first hop of this payment. These records will be transmitted via the - // wire message only and therefore do not affect the onion payload size. - FirstHopCustomRecords lnwire.CustomRecords -} - -// String returns a human-readable description of the payment creation info. -func (p *PaymentCreationInfo) String() string { - return fmt.Sprintf("payment_id=%v, amount=%v, created_at=%v", - p.PaymentIdentifier, p.Value, p.CreationTime) -} - -// PaymentsQuery represents a query to the payments database starting or ending -// at a certain offset index. The number of retrieved records can be limited. -type PaymentsQuery struct { - // IndexOffset determines the starting point of the payments query and - // is always exclusive. In normal order, the query starts at the next - // higher (available) index compared to IndexOffset. In reversed order, - // the query ends at the next lower (available) index compared to the - // IndexOffset. In the case of a zero index_offset, the query will start - // with the oldest payment when paginating forwards, or will end with - // the most recent payment when paginating backwards. - IndexOffset uint64 - - // MaxPayments is the maximal number of payments returned in the - // payments query. - MaxPayments uint64 - - // Reversed gives a meaning to the IndexOffset. If reversed is set to - // true, the query will fetch payments with indices lower than the - // IndexOffset, otherwise, it will return payments with indices greater - // than the IndexOffset. - Reversed bool - - // If IncludeIncomplete is true, then return payments that have not yet - // fully completed. This means that pending payments, as well as failed - // payments will show up if this field is set to true. - IncludeIncomplete bool - - // CountTotal indicates that all payments currently present in the - // payment index (complete and incomplete) should be counted. - CountTotal bool - - // CreationDateStart, expressed in Unix seconds, if set, filters out - // all payments with a creation date greater than or equal to it. - CreationDateStart int64 - - // CreationDateEnd, expressed in Unix seconds, if set, filters out all - // payments with a creation date less than or equal to it. - CreationDateEnd int64 -} - -// PaymentsResponse contains the result of a query to the payments database. -// It includes the set of payments that match the query and integers which -// represent the index of the first and last item returned in the series of -// payments. These integers allow callers to resume their query in the event -// that the query's response exceeds the max number of returnable events. -type PaymentsResponse struct { - // Payments is the set of payments returned from the database for the - // PaymentsQuery. - Payments []*MPPayment - - // FirstIndexOffset is the index of the first element in the set of - // returned MPPayments. Callers can use this to resume their query - // in the event that the slice has too many events to fit into a single - // response. The offset can be used to continue reverse pagination. - FirstIndexOffset uint64 - - // LastIndexOffset is the index of the last element in the set of - // returned MPPayments. Callers can use this to resume their query - // in the event that the slice has too many events to fit into a single - // response. The offset can be used to continue forward pagination. - LastIndexOffset uint64 - - // TotalCount represents the total number of payments that are currently - // stored in the payment database. This will only be set if the - // CountTotal field in the query was set to true. - TotalCount uint64 -} diff --git a/channeldb/payments_test.go b/channeldb/payments_test.go deleted file mode 100644 index dce993ffe5d..00000000000 --- a/channeldb/payments_test.go +++ /dev/null @@ -1,474 +0,0 @@ -package channeldb - -import ( - "context" - "fmt" - "math" - "reflect" - "testing" - "time" - - "github.com/btcsuite/btcd/btcec/v2" - "github.com/davecgh/go-spew/spew" - "github.com/lightningnetwork/lnd/record" - "github.com/lightningnetwork/lnd/routing/route" - "github.com/stretchr/testify/require" -) - -var ( - priv, _ = btcec.NewPrivateKey() - pub = priv.PubKey() - vertex = route.NewVertex(pub) - - testHop1 = &route.Hop{ - PubKeyBytes: vertex, - ChannelID: 12345, - OutgoingTimeLock: 111, - AmtToForward: 555, - CustomRecords: record.CustomSet{ - 65536: []byte{}, - 80001: []byte{}, - }, - MPP: record.NewMPP(32, [32]byte{0x42}), - Metadata: []byte{1, 2, 3}, - } - - testHop2 = &route.Hop{ - PubKeyBytes: vertex, - ChannelID: 12345, - OutgoingTimeLock: 111, - AmtToForward: 555, - LegacyPayload: true, - } - - testHop3 = &route.Hop{ - PubKeyBytes: route.NewVertex(pub), - ChannelID: 12345, - OutgoingTimeLock: 111, - AmtToForward: 555, - CustomRecords: record.CustomSet{ - 65536: []byte{}, - 80001: []byte{}, - }, - AMP: record.NewAMP([32]byte{0x69}, [32]byte{0x42}, 1), - Metadata: []byte{1, 2, 3}, - } - - testRoute = route.Route{ - TotalTimeLock: 123, - TotalAmount: 1234567, - SourcePubKey: vertex, - Hops: []*route.Hop{ - testHop2, - testHop1, - }, - } - - testBlindedRoute = route.Route{ - TotalTimeLock: 150, - TotalAmount: 1000, - SourcePubKey: vertex, - Hops: []*route.Hop{ - { - PubKeyBytes: vertex, - ChannelID: 9876, - OutgoingTimeLock: 120, - AmtToForward: 900, - EncryptedData: []byte{1, 3, 3}, - BlindingPoint: pub, - }, - { - PubKeyBytes: vertex, - EncryptedData: []byte{3, 2, 1}, - }, - { - PubKeyBytes: vertex, - Metadata: []byte{4, 5, 6}, - AmtToForward: 500, - OutgoingTimeLock: 100, - TotalAmtMsat: 500, - }, - }, - } -) - -// assertRouteEquals compares to routes for equality and returns an error if -// they are not equal. -func assertRouteEqual(a, b *route.Route) error { - if !reflect.DeepEqual(a, b) { - return fmt.Errorf("HTLCAttemptInfos don't match: %v vs %v", - spew.Sdump(a), spew.Sdump(b)) - } - - return nil -} - -// TestQueryPayments tests retrieval of payments with forwards and reversed -// queries. -func TestQueryPayments(t *testing.T) { - // Define table driven test for QueryPayments. - // Test payments have sequence indices [1, 3, 4, 5, 6, 7]. - // Note that the payment with index 7 has the same payment hash as 6, - // and is stored in a nested bucket within payment 6 rather than being - // its own entry in the payments bucket. We do this to test retrieval - // of legacy payments. - tests := []struct { - name string - query PaymentsQuery - firstIndex uint64 - lastIndex uint64 - - // expectedSeqNrs contains the set of sequence numbers we expect - // our query to return. - expectedSeqNrs []uint64 - }{ - { - name: "IndexOffset at the end of the payments range", - query: PaymentsQuery{ - IndexOffset: 7, - MaxPayments: 7, - Reversed: false, - IncludeIncomplete: true, - }, - firstIndex: 0, - lastIndex: 0, - expectedSeqNrs: nil, - }, - { - name: "query in forwards order, start at beginning", - query: PaymentsQuery{ - IndexOffset: 0, - MaxPayments: 2, - Reversed: false, - IncludeIncomplete: true, - }, - firstIndex: 1, - lastIndex: 3, - expectedSeqNrs: []uint64{1, 3}, - }, - { - name: "query in forwards order, start at end, overflow", - query: PaymentsQuery{ - IndexOffset: 6, - MaxPayments: 2, - Reversed: false, - IncludeIncomplete: true, - }, - firstIndex: 7, - lastIndex: 7, - expectedSeqNrs: []uint64{7}, - }, - { - name: "start at offset index outside of payments", - query: PaymentsQuery{ - IndexOffset: 20, - MaxPayments: 2, - Reversed: false, - IncludeIncomplete: true, - }, - firstIndex: 0, - lastIndex: 0, - expectedSeqNrs: nil, - }, - { - name: "overflow in forwards order", - query: PaymentsQuery{ - IndexOffset: 4, - MaxPayments: math.MaxUint64, - Reversed: false, - IncludeIncomplete: true, - }, - firstIndex: 5, - lastIndex: 7, - expectedSeqNrs: []uint64{5, 6, 7}, - }, - { - name: "start at offset index outside of payments, " + - "reversed order", - query: PaymentsQuery{ - IndexOffset: 9, - MaxPayments: 2, - Reversed: true, - IncludeIncomplete: true, - }, - firstIndex: 6, - lastIndex: 7, - expectedSeqNrs: []uint64{6, 7}, - }, - { - name: "query in reverse order, start at end", - query: PaymentsQuery{ - IndexOffset: 0, - MaxPayments: 2, - Reversed: true, - IncludeIncomplete: true, - }, - firstIndex: 6, - lastIndex: 7, - expectedSeqNrs: []uint64{6, 7}, - }, - { - name: "query in reverse order, starting in middle", - query: PaymentsQuery{ - IndexOffset: 4, - MaxPayments: 2, - Reversed: true, - IncludeIncomplete: true, - }, - firstIndex: 1, - lastIndex: 3, - expectedSeqNrs: []uint64{1, 3}, - }, - { - name: "query in reverse order, starting in middle, " + - "with underflow", - query: PaymentsQuery{ - IndexOffset: 4, - MaxPayments: 5, - Reversed: true, - IncludeIncomplete: true, - }, - firstIndex: 1, - lastIndex: 3, - expectedSeqNrs: []uint64{1, 3}, - }, - { - name: "all payments in reverse, order maintained", - query: PaymentsQuery{ - IndexOffset: 0, - MaxPayments: 7, - Reversed: true, - IncludeIncomplete: true, - }, - firstIndex: 1, - lastIndex: 7, - expectedSeqNrs: []uint64{1, 3, 4, 5, 6, 7}, - }, - { - name: "exclude incomplete payments", - query: PaymentsQuery{ - IndexOffset: 0, - MaxPayments: 7, - Reversed: false, - IncludeIncomplete: false, - }, - firstIndex: 7, - lastIndex: 7, - expectedSeqNrs: []uint64{7}, - }, - { - name: "query payments at index gap", - query: PaymentsQuery{ - IndexOffset: 1, - MaxPayments: 7, - Reversed: false, - IncludeIncomplete: true, - }, - firstIndex: 3, - lastIndex: 7, - expectedSeqNrs: []uint64{3, 4, 5, 6, 7}, - }, - { - name: "query payments reverse before index gap", - query: PaymentsQuery{ - IndexOffset: 3, - MaxPayments: 7, - Reversed: true, - IncludeIncomplete: true, - }, - firstIndex: 1, - lastIndex: 1, - expectedSeqNrs: []uint64{1}, - }, - { - name: "query payments reverse on index gap", - query: PaymentsQuery{ - IndexOffset: 2, - MaxPayments: 7, - Reversed: true, - IncludeIncomplete: true, - }, - firstIndex: 1, - lastIndex: 1, - expectedSeqNrs: []uint64{1}, - }, - { - name: "query payments forward on index gap", - query: PaymentsQuery{ - IndexOffset: 2, - MaxPayments: 2, - Reversed: false, - IncludeIncomplete: true, - }, - firstIndex: 3, - lastIndex: 4, - expectedSeqNrs: []uint64{3, 4}, - }, - { - name: "query in forwards order, with start creation " + - "time", - query: PaymentsQuery{ - IndexOffset: 0, - MaxPayments: 2, - Reversed: false, - IncludeIncomplete: true, - CreationDateStart: 5, - }, - firstIndex: 5, - lastIndex: 6, - expectedSeqNrs: []uint64{5, 6}, - }, - { - name: "query in forwards order, with start creation " + - "time at end, overflow", - query: PaymentsQuery{ - IndexOffset: 0, - MaxPayments: 2, - Reversed: false, - IncludeIncomplete: true, - CreationDateStart: 7, - }, - firstIndex: 7, - lastIndex: 7, - expectedSeqNrs: []uint64{7}, - }, - { - name: "query with start and end creation time", - query: PaymentsQuery{ - IndexOffset: 9, - MaxPayments: math.MaxUint64, - Reversed: true, - IncludeIncomplete: true, - CreationDateStart: 3, - CreationDateEnd: 5, - }, - firstIndex: 3, - lastIndex: 5, - expectedSeqNrs: []uint64{3, 4, 5}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - ctx := context.Background() - - db, err := MakeTestDB(t) - require.NoError(t, err) - - // Initialize the payment database. - paymentDB, err := NewKVPaymentsDB(db) - require.NoError(t, err) - - // Make a preliminary query to make sure it's ok to - // query when we have no payments. - resp, err := paymentDB.QueryPayments(ctx, tt.query) - require.NoError(t, err) - require.Len(t, resp.Payments, 0) - - // Populate the database with a set of test payments. - // We create 6 original payments, deleting the payment - // at index 2 so that we cover the case where sequence - // numbers are missing. We also add a duplicate payment - // to the last payment added to test the legacy case - // where we have duplicates in the nested duplicates - // bucket. - nonDuplicatePayments := 6 - - for i := 0; i < nonDuplicatePayments; i++ { - // Generate a test payment. - info, _, preimg, err := genInfo(t) - if err != nil { - t.Fatalf("unable to create test "+ - "payment: %v", err) - } - // Override creation time to allow for testing - // of CreationDateStart and CreationDateEnd. - info.CreationTime = time.Unix(int64(i+1), 0) - - // Create a new payment entry in the database. - err = paymentDB.InitPayment( - info.PaymentIdentifier, info, - ) - require.NoError(t, err) - - // Immediately delete the payment with index 2. - if i == 1 { - pmt, err := paymentDB.FetchPayment( - info.PaymentIdentifier, - ) - require.NoError(t, err) - - deletePayment( - t, db, info.PaymentIdentifier, - pmt.SequenceNum, - ) - } - - // If we are on the last payment entry, add a - // duplicate payment with sequence number equal - // to the parent payment + 1. Note that - // duplicate payments will always be succeeded. - if i == (nonDuplicatePayments - 1) { - pmt, err := paymentDB.FetchPayment( - info.PaymentIdentifier, - ) - require.NoError(t, err) - - appendDuplicatePayment( - t, paymentDB.db, - info.PaymentIdentifier, - pmt.SequenceNum+1, - preimg, - ) - } - } - - // Fetch all payments in the database. - allPayments, err := paymentDB.FetchPayments() - if err != nil { - t.Fatalf("payments could not be fetched from "+ - "database: %v", err) - } - - if len(allPayments) != 6 { - t.Fatalf("Number of payments received does "+ - "not match expected one. Got %v, "+ - "want %v.", len(allPayments), 6) - } - - querySlice, err := paymentDB.QueryPayments( - ctx, tt.query, - ) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if tt.firstIndex != querySlice.FirstIndexOffset || - tt.lastIndex != querySlice.LastIndexOffset { - - t.Errorf("First or last index does not match "+ - "expected index. Want (%d, %d), "+ - "got (%d, %d).", - tt.firstIndex, tt.lastIndex, - querySlice.FirstIndexOffset, - querySlice.LastIndexOffset) - } - - if len(querySlice.Payments) != len(tt.expectedSeqNrs) { - t.Errorf("expected: %v payments, got: %v", - len(tt.expectedSeqNrs), - len(querySlice.Payments)) - } - - for i, seqNr := range tt.expectedSeqNrs { - q := querySlice.Payments[i] - if seqNr != q.SequenceNum { - t.Errorf("sequence numbers do not "+ - "match, got %v, want %v", - q.SequenceNum, seqNr) - } - } - }) - } -} diff --git a/config_builder.go b/config_builder.go index 5a2406057c2..be56f962b59 100644 --- a/config_builder.go +++ b/config_builder.go @@ -927,7 +927,7 @@ type DatabaseInstances struct { // KVPaymentsDB is the database that stores all payment related // information. - KVPaymentsDB *channeldb.KVPaymentsDB + KVPaymentsDB *paymentsdb.KVPaymentsDB // MacaroonDB is the database that stores macaroon root keys. MacaroonDB kvdb.Backend @@ -1225,7 +1225,7 @@ func (d *DefaultDatabaseBuilder) BuildDatabase( cfg.KeepFailedPaymentAttempts, ), } - kvPaymentsDB, err := channeldb.NewKVPaymentsDB( + kvPaymentsDB, err := paymentsdb.NewKVPaymentsDB( dbs.ChanStateDB, paymentsDBOptions..., ) diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index 34cd7047e7b..022f4619acc 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -14,7 +14,6 @@ import ( "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/wire" sphinx "github.com/lightningnetwork/lightning-onion" - "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/feature" "github.com/lightningnetwork/lnd/fn/v2" @@ -22,6 +21,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" + paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing/route" @@ -1488,7 +1488,7 @@ func UnmarshalAMP(reqAMP *lnrpc.AMPRecord) (*record.AMP, error) { // MarshalHTLCAttempt constructs an RPC HTLCAttempt from the db representation. func (r *RouterBackend) MarshalHTLCAttempt( - htlc channeldb.HTLCAttempt) (*lnrpc.HTLCAttempt, error) { + htlc paymentsdb.HTLCAttempt) (*lnrpc.HTLCAttempt, error) { route, err := r.MarshallRoute(&htlc.Route) if err != nil { @@ -1529,7 +1529,7 @@ func (r *RouterBackend) MarshalHTLCAttempt( // marshallHtlcFailure marshalls htlc fail info from the database to its rpc // representation. -func marshallHtlcFailure(failure *channeldb.HTLCFailInfo) (*lnrpc.Failure, +func marshallHtlcFailure(failure *paymentsdb.HTLCFailInfo) (*lnrpc.Failure, error) { rpcFailure := &lnrpc.Failure{ @@ -1537,16 +1537,16 @@ func marshallHtlcFailure(failure *channeldb.HTLCFailInfo) (*lnrpc.Failure, } switch failure.Reason { - case channeldb.HTLCFailUnknown: + case paymentsdb.HTLCFailUnknown: rpcFailure.Code = lnrpc.Failure_UNKNOWN_FAILURE - case channeldb.HTLCFailUnreadable: + case paymentsdb.HTLCFailUnreadable: rpcFailure.Code = lnrpc.Failure_UNREADABLE_FAILURE - case channeldb.HTLCFailInternal: + case paymentsdb.HTLCFailInternal: rpcFailure.Code = lnrpc.Failure_INTERNAL_FAILURE - case channeldb.HTLCFailMessage: + case paymentsdb.HTLCFailMessage: err := marshallWireError(failure.Message, rpcFailure) if err != nil { return nil, err @@ -1743,7 +1743,7 @@ func marshallChannelUpdate(update *lnwire.ChannelUpdate1) *lnrpc.ChannelUpdate { } // MarshallPayment marshall a payment to its rpc representation. -func (r *RouterBackend) MarshallPayment(payment *channeldb.MPPayment) ( +func (r *RouterBackend) MarshallPayment(payment *paymentsdb.MPPayment) ( *lnrpc.Payment, error) { // Fetch the payment's preimage and the total paid in fees. @@ -1813,11 +1813,11 @@ func (r *RouterBackend) MarshallPayment(payment *channeldb.MPPayment) ( // convertPaymentStatus converts a channeldb.PaymentStatus to the type expected // by the RPC. -func convertPaymentStatus(dbStatus channeldb.PaymentStatus, useInit bool) ( +func convertPaymentStatus(dbStatus paymentsdb.PaymentStatus, useInit bool) ( lnrpc.Payment_PaymentStatus, error) { switch dbStatus { - case channeldb.StatusInitiated: + case paymentsdb.StatusInitiated: // If the client understands the new status, return it. if useInit { return lnrpc.Payment_INITIATED, nil @@ -1826,13 +1826,13 @@ func convertPaymentStatus(dbStatus channeldb.PaymentStatus, useInit bool) ( // Otherwise remain the old behavior. return lnrpc.Payment_IN_FLIGHT, nil - case channeldb.StatusInFlight: + case paymentsdb.StatusInFlight: return lnrpc.Payment_IN_FLIGHT, nil - case channeldb.StatusSucceeded: + case paymentsdb.StatusSucceeded: return lnrpc.Payment_SUCCEEDED, nil - case channeldb.StatusFailed: + case paymentsdb.StatusFailed: return lnrpc.Payment_FAILED, nil default: @@ -1842,7 +1842,7 @@ func convertPaymentStatus(dbStatus channeldb.PaymentStatus, useInit bool) ( // marshallPaymentFailureReason marshalls the failure reason to the corresponding rpc // type. -func marshallPaymentFailureReason(reason *channeldb.FailureReason) ( +func marshallPaymentFailureReason(reason *paymentsdb.FailureReason) ( lnrpc.PaymentFailureReason, error) { if reason == nil { @@ -1850,22 +1850,22 @@ func marshallPaymentFailureReason(reason *channeldb.FailureReason) ( } switch *reason { - case channeldb.FailureReasonTimeout: + case paymentsdb.FailureReasonTimeout: return lnrpc.PaymentFailureReason_FAILURE_REASON_TIMEOUT, nil - case channeldb.FailureReasonNoRoute: + case paymentsdb.FailureReasonNoRoute: return lnrpc.PaymentFailureReason_FAILURE_REASON_NO_ROUTE, nil - case channeldb.FailureReasonError: + case paymentsdb.FailureReasonError: return lnrpc.PaymentFailureReason_FAILURE_REASON_ERROR, nil - case channeldb.FailureReasonPaymentDetails: + case paymentsdb.FailureReasonPaymentDetails: return lnrpc.PaymentFailureReason_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS, nil - case channeldb.FailureReasonInsufficientBalance: + case paymentsdb.FailureReasonInsufficientBalance: return lnrpc.PaymentFailureReason_FAILURE_REASON_INSUFFICIENT_BALANCE, nil - case channeldb.FailureReasonCanceled: + case paymentsdb.FailureReasonCanceled: return lnrpc.PaymentFailureReason_FAILURE_REASON_CANCELED, nil } diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 843222eee4b..894edf28aa6 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -15,7 +15,6 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/lightningnetwork/lnd/aliasmgr" - "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" @@ -927,7 +926,7 @@ func (s *Server) SendToRouteV2(ctx context.Context, return nil, err } - var attempt *channeldb.HTLCAttempt + var attempt *paymentsdb.HTLCAttempt // Pass route to the router. This call returns the full htlc attempt // information as it is stored in the database. It is possible that both @@ -1449,17 +1448,21 @@ func (s *Server) trackPaymentStream(context context.Context, // No more payment updates. return nil } - result := item.(*channeldb.MPPayment) + result, ok := item.(*paymentsdb.MPPayment) + if !ok { + return fmt.Errorf("unexpected payment type: %T", + item) + } log.Tracef("Payment %v updated to state %v", result.Info.PaymentIdentifier, result.Status) // Skip in-flight updates unless requested. if noInflightUpdates { - if result.Status == channeldb.StatusInitiated { + if result.Status == paymentsdb.StatusInitiated { continue } - if result.Status == channeldb.StatusInFlight { + if result.Status == paymentsdb.StatusInFlight { continue } } diff --git a/lnrpc/routerrpc/router_server_test.go b/lnrpc/routerrpc/router_server_test.go index bc5a7f16d9a..70e3fbda89e 100644 --- a/lnrpc/routerrpc/router_server_test.go +++ b/lnrpc/routerrpc/router_server_test.go @@ -6,10 +6,10 @@ import ( "time" "github.com/btcsuite/btcd/btcec/v2" - "github.com/lightningnetwork/lnd/channeldb" graphdb "github.com/lightningnetwork/lnd/graph/db" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwire" + paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/lightningnetwork/lnd/queue" "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing/route" @@ -129,13 +129,13 @@ func TestTrackPaymentsInflightUpdates(t *testing.T) { }() // Enqueue some payment updates on the mock. - towerMock.queue.ChanIn() <- &channeldb.MPPayment{ - Info: &channeldb.PaymentCreationInfo{}, - Status: channeldb.StatusInFlight, + towerMock.queue.ChanIn() <- &paymentsdb.MPPayment{ + Info: &paymentsdb.PaymentCreationInfo{}, + Status: paymentsdb.StatusInFlight, } - towerMock.queue.ChanIn() <- &channeldb.MPPayment{ - Info: &channeldb.PaymentCreationInfo{}, - Status: channeldb.StatusSucceeded, + towerMock.queue.ChanIn() <- &paymentsdb.MPPayment{ + Info: &paymentsdb.PaymentCreationInfo{}, + Status: paymentsdb.StatusSucceeded, } // Wait until there's 2 updates or the deadline is exceeded. @@ -191,13 +191,13 @@ func TestTrackPaymentsNoInflightUpdates(t *testing.T) { }() // Enqueue some payment updates on the mock. - towerMock.queue.ChanIn() <- &channeldb.MPPayment{ - Info: &channeldb.PaymentCreationInfo{}, - Status: channeldb.StatusInFlight, + towerMock.queue.ChanIn() <- &paymentsdb.MPPayment{ + Info: &paymentsdb.PaymentCreationInfo{}, + Status: paymentsdb.StatusInFlight, } - towerMock.queue.ChanIn() <- &channeldb.MPPayment{ - Info: &channeldb.PaymentCreationInfo{}, - Status: channeldb.StatusSucceeded, + towerMock.queue.ChanIn() <- &paymentsdb.MPPayment{ + Info: &paymentsdb.PaymentCreationInfo{}, + Status: paymentsdb.StatusSucceeded, } // Wait until there's 1 update or the deadline is exceeded. diff --git a/payments/db/codec.go b/payments/db/codec.go new file mode 100644 index 00000000000..cd9c0c7d0ac --- /dev/null +++ b/payments/db/codec.go @@ -0,0 +1,141 @@ +package paymentsdb + +import ( + "encoding/binary" + "errors" + "io" + "time" + + "github.com/lightningnetwork/lnd/channeldb" +) + +// Big endian is the preferred byte order, due to cursor scans over +// integer keys iterating in order. +var byteOrder = binary.BigEndian + +// UnknownElementType is an alias for channeldb.UnknownElementType. +type UnknownElementType = channeldb.UnknownElementType + +// ReadElement deserializes a single element from the provided io.Reader. +func ReadElement(r io.Reader, element interface{}) error { + err := channeldb.ReadElement(r, element) + switch { + // Known to channeldb codec. + case err == nil: + return nil + + // Fail if error is not UnknownElementType. + default: + var unknownElementType UnknownElementType + if !errors.As(err, &unknownElementType) { + return err + } + } + + // Process any paymentsdb-specific extensions to the codec. + switch e := element.(type) { + case *paymentIndexType: + if err := binary.Read(r, byteOrder, e); err != nil { + return err + } + + // Type is still unknown to paymentsdb extensions, fail. + default: + return channeldb.NewUnknownElementType( + "ReadElement", element, + ) + } + + return nil +} + +// WriteElement serializes a single element into the provided io.Writer. +func WriteElement(w io.Writer, element interface{}) error { + err := channeldb.WriteElement(w, element) + switch { + // Known to channeldb codec. + case err == nil: + return nil + + // Fail if error is not UnknownElementType. + default: + var unknownElementType UnknownElementType + if !errors.As(err, &unknownElementType) { + return err + } + } + + // Process any paymentsdb-specific extensions to the codec. + switch e := element.(type) { + case paymentIndexType: + if err := binary.Write(w, byteOrder, e); err != nil { + return err + } + + // Type is still unknown to paymentsdb extensions, fail. + default: + return channeldb.NewUnknownElementType( + "WriteElement", element, + ) + } + + return nil +} + +// WriteElements serializes a variadic list of elements into the given +// io.Writer. +func WriteElements(w io.Writer, elements ...interface{}) error { + for _, element := range elements { + if err := WriteElement(w, element); err != nil { + return err + } + } + + return nil +} + +// ReadElements deserializes the provided io.Reader into a variadic list of +// target elements. +func ReadElements(r io.Reader, elements ...interface{}) error { + for _, element := range elements { + if err := ReadElement(r, element); err != nil { + return err + } + } + + return nil +} + +// deserializeTime deserializes time as unix nanoseconds. +func deserializeTime(r io.Reader) (time.Time, error) { + var scratch [8]byte + if _, err := io.ReadFull(r, scratch[:]); err != nil { + return time.Time{}, err + } + + // Convert to time.Time. Interpret unix nano time zero as a zero + // time.Time value. + unixNano := byteOrder.Uint64(scratch[:]) + if unixNano == 0 { + return time.Time{}, nil + } + + return time.Unix(0, int64(unixNano)), nil +} + +// serializeTime serializes time as unix nanoseconds. +func serializeTime(w io.Writer, t time.Time) error { + var scratch [8]byte + + // Convert to unix nano seconds, but only if time is non-zero. Calling + // UnixNano() on a zero time yields an undefined result. + var unixNano int64 + if !t.IsZero() { + unixNano = t.UnixNano() + } + + byteOrder.PutUint64(scratch[:], uint64(unixNano)) + _, err := w.Write(scratch[:]) + + return err +} diff --git a/channeldb/duplicate_payments.go b/payments/db/kv_duplicate_payments.go similarity index 98% rename from channeldb/duplicate_payments.go rename to payments/db/kv_duplicate_payments.go index 004722f0067..3ac1faddd60 100644 --- a/channeldb/duplicate_payments.go +++ b/payments/db/kv_duplicate_payments.go @@ -1,4 +1,4 @@ -package channeldb +package paymentsdb import ( "bytes" @@ -11,7 +11,6 @@ import ( "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" - paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/lightningnetwork/lnd/routing/route" ) @@ -75,7 +74,7 @@ func fetchDuplicatePaymentStatus(bucket kvdb.RBucket) (PaymentStatus, error) { return StatusInFlight, nil } - return 0, paymentsdb.ErrPaymentNotInitiated + return 0, ErrPaymentNotInitiated } func deserializeDuplicateHTLCAttemptInfo(r io.Reader) ( diff --git a/channeldb/payments_kv_store.go b/payments/db/kv_store.go similarity index 97% rename from channeldb/payments_kv_store.go rename to payments/db/kv_store.go index cf2ca918b98..afc7dec0ac0 100644 --- a/channeldb/payments_kv_store.go +++ b/payments/db/kv_store.go @@ -1,4 +1,4 @@ -package channeldb +package paymentsdb import ( "bytes" @@ -13,10 +13,10 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" - paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/tlv" @@ -127,19 +127,21 @@ type KVPaymentsDB struct { // db is the underlying database implementation. db kvdb.Backend + // keepFailedPaymentAttempts is a flag that indicates whether we should + // keep failed payment attempts in the database. keepFailedPaymentAttempts bool } // defaultKVStoreOptions returns the default options for the KV store. -func defaultKVStoreOptions() *paymentsdb.StoreOptions { - return &paymentsdb.StoreOptions{ +func defaultKVStoreOptions() *StoreOptions { + return &StoreOptions{ KeepFailedPaymentAttempts: false, } } // NewKVPaymentsDB creates a new KVStore for payments. func NewKVPaymentsDB(db kvdb.Backend, - options ...paymentsdb.OptionModifier) (*KVPaymentsDB, error) { + options ...OptionModifier) (*KVPaymentsDB, error) { opts := defaultKVStoreOptions() for _, applyOption := range options { @@ -158,6 +160,8 @@ func NewKVPaymentsDB(db kvdb.Backend, }, nil } +// paymentsTopLevelBuckets is a list of top-level buckets that are used for +// the payments database when using the kv store. var paymentsTopLevelBuckets = [][]byte{ paymentsRootBucket, paymentsIndexBucket, @@ -229,7 +233,7 @@ func (p *KVPaymentsDB) InitPayment(paymentHash lntypes.Hash, // Otherwise, if the error is not `ErrPaymentNotInitiated`, // we'll return the error. - case !errors.Is(err, paymentsdb.ErrPaymentNotInitiated): + case !errors.Is(err, ErrPaymentNotInitiated): return err } @@ -403,7 +407,7 @@ func (p *KVPaymentsDB) RegisterAttempt(paymentHash lntypes.Hash, // MPP records should not be set for attempts to blinded paths. if isBlinded && mpp != nil { - return paymentsdb.ErrMPPRecordInBlindedPayment + return ErrMPPRecordInBlindedPayment } for _, h := range payment.InFlightHTLCs() { @@ -412,7 +416,7 @@ func (p *KVPaymentsDB) RegisterAttempt(paymentHash lntypes.Hash, // If this is a blinded payment, then no existing HTLCs // should have MPP records. if isBlinded && hMpp != nil { - return paymentsdb.ErrMPPRecordInBlindedPayment + return ErrMPPRecordInBlindedPayment } // If this is a blinded payment, then we just need to @@ -424,7 +428,7 @@ func (p *KVPaymentsDB) RegisterAttempt(paymentHash lntypes.Hash, h.Route.FinalHop().TotalAmtMsat { //nolint:ll - return paymentsdb.ErrBlindedPaymentTotalAmountMismatch + return ErrBlindedPaymentTotalAmountMismatch } continue @@ -434,12 +438,12 @@ func (p *KVPaymentsDB) RegisterAttempt(paymentHash lntypes.Hash, // We tried to register a non-MPP attempt for a MPP // payment. case mpp == nil && hMpp != nil: - return paymentsdb.ErrMPPayment + return ErrMPPayment // We tried to register a MPP shard for a non-MPP // payment. case mpp != nil && hMpp == nil: - return paymentsdb.ErrNonMPPayment + return ErrNonMPPayment // Non-MPP payment, nothing more to validate. case mpp == nil: @@ -448,11 +452,11 @@ func (p *KVPaymentsDB) RegisterAttempt(paymentHash lntypes.Hash, // Check that MPP options match. if mpp.PaymentAddr() != hMpp.PaymentAddr() { - return paymentsdb.ErrMPPPaymentAddrMismatch + return ErrMPPPaymentAddrMismatch } if mpp.TotalMsat() != hMpp.TotalMsat() { - return paymentsdb.ErrMPPTotalAmountMismatch + return ErrMPPTotalAmountMismatch } } @@ -461,14 +465,14 @@ func (p *KVPaymentsDB) RegisterAttempt(paymentHash lntypes.Hash, // attempt. amt := attempt.Route.ReceiverAmt() if !isBlinded && mpp == nil && amt != payment.Info.Value { - return paymentsdb.ErrValueMismatch + return ErrValueMismatch } // Ensure we aren't sending more than the total payment amount. sentAmt, _ := payment.SentAmt() if sentAmt+amt > payment.Info.Value { return fmt.Errorf("%w: attempted=%v, payment amount="+ - "%v", paymentsdb.ErrValueExceedsAmt, + "%v", ErrValueExceedsAmt, sentAmt+amt, payment.Info.Value) } @@ -574,12 +578,12 @@ func (p *KVPaymentsDB) updateHtlcKey(paymentHash lntypes.Hash, // Make sure the shard is not already failed or settled. failKey := htlcBucketKey(htlcFailInfoKey, aid) if htlcsBucket.Get(failKey) != nil { - return paymentsdb.ErrAttemptAlreadyFailed + return ErrAttemptAlreadyFailed } settleKey := htlcBucketKey(htlcSettleInfoKey, aid) if htlcsBucket.Get(settleKey) != nil { - return paymentsdb.ErrAttemptAlreadySettled + return ErrAttemptAlreadySettled } // Add or update the key for this htlc. @@ -619,8 +623,8 @@ func (p *KVPaymentsDB) Fail(paymentHash lntypes.Hash, prefetchPayment(tx, paymentHash) bucket, err := fetchPaymentBucketUpdate(tx, paymentHash) - if errors.Is(err, paymentsdb.ErrPaymentNotInitiated) { - updateErr = paymentsdb.ErrPaymentNotInitiated + if errors.Is(err, ErrPaymentNotInitiated) { + updateErr = ErrPaymentNotInitiated return nil } else if err != nil { return err @@ -631,8 +635,8 @@ func (p *KVPaymentsDB) Fail(paymentHash lntypes.Hash, // failure to the KVPaymentsDB without synchronizing with // other attempts. _, err = fetchPaymentStatus(bucket) - if errors.Is(err, paymentsdb.ErrPaymentNotInitiated) { - updateErr = paymentsdb.ErrPaymentNotInitiated + if errors.Is(err, ErrPaymentNotInitiated) { + updateErr = ErrPaymentNotInitiated return nil } else if err != nil { return err @@ -725,12 +729,12 @@ func fetchPaymentBucket(tx kvdb.RTx, paymentHash lntypes.Hash) ( payments := tx.ReadBucket(paymentsRootBucket) if payments == nil { - return nil, paymentsdb.ErrPaymentNotInitiated + return nil, ErrPaymentNotInitiated } bucket := payments.NestedReadBucket(paymentHash[:]) if bucket == nil { - return nil, paymentsdb.ErrPaymentNotInitiated + return nil, ErrPaymentNotInitiated } return bucket, nil @@ -743,12 +747,12 @@ func fetchPaymentBucketUpdate(tx kvdb.RwTx, paymentHash lntypes.Hash) ( payments := tx.ReadWriteBucket(paymentsRootBucket) if payments == nil { - return nil, paymentsdb.ErrPaymentNotInitiated + return nil, ErrPaymentNotInitiated } bucket := payments.NestedReadWriteBucket(paymentHash[:]) if bucket == nil { - return nil, paymentsdb.ErrPaymentNotInitiated + return nil, ErrPaymentNotInitiated } return bucket, nil @@ -805,7 +809,7 @@ func fetchPaymentStatus(bucket kvdb.RBucket) (PaymentStatus, error) { // Creation info should be set for all payments, regardless of state. // If not, it is unknown. if bucket.Get(paymentCreationInfoKey) == nil { - return 0, paymentsdb.ErrPaymentNotInitiated + return 0, ErrPaymentNotInitiated } payment, err := fetchPayment(bucket) @@ -1051,7 +1055,7 @@ func fetchHtlcAttempts(bucket kvdb.RBucket) ([]HTLCAttempt, error) { // Sanity check that all htlcs have an attempt info. if attemptInfoCount != len(htlcsMap) { - return nil, paymentsdb.ErrNoAttemptInfo + return nil, ErrNoAttemptInfo } keys := make([]uint64, len(htlcsMap)) @@ -1131,9 +1135,9 @@ func fetchFailedHtlcKeys(bucket kvdb.RBucket) ([][]byte, error) { // to a subset of payments by the payments query, containing an offset // index and a maximum number of returned payments. func (p *KVPaymentsDB) QueryPayments(_ context.Context, - query PaymentsQuery) (PaymentsResponse, error) { + query Query) (Response, error) { - var resp PaymentsResponse + var resp Response if err := kvdb.View(p.db, func(tx kvdb.RTx) error { // Get the root payments bucket. @@ -1205,13 +1209,13 @@ func (p *KVPaymentsDB) QueryPayments(_ context.Context, // Create a paginator which reads from our sequence index bucket // with the parameters provided by the payments query. - paginator := newPaginator( + paginator := channeldb.NewPaginator( indexes.ReadCursor(), query.Reversed, query.IndexOffset, query.MaxPayments, ) // Run a paginated query, adding payments to our response. - if err := paginator.query(accumulatePayments); err != nil { + if err := paginator.Query(accumulatePayments); err != nil { return err } @@ -1247,7 +1251,7 @@ func (p *KVPaymentsDB) QueryPayments(_ context.Context, return nil }, func() { - resp = PaymentsResponse{} + resp = Response{} }); err != nil { return resp, err } @@ -1290,7 +1294,7 @@ func fetchPaymentWithSequenceNumber(tx kvdb.RTx, paymentHash lntypes.Hash, // the payment we are actually looking for. seqBytes := bucket.Get(paymentSequenceKey) if seqBytes == nil { - return nil, paymentsdb.ErrNoSequenceNumber + return nil, ErrNoSequenceNumber } // If this top level payment has the sequence number we are looking for, @@ -1305,7 +1309,7 @@ func fetchPaymentWithSequenceNumber(tx kvdb.RTx, paymentHash lntypes.Hash, // find a duplicate payments bucket here, something is wrong. dup := bucket.NestedReadBucket(duplicatePaymentsBucket) if dup == nil { - return nil, paymentsdb.ErrNoDuplicateBucket + return nil, ErrNoDuplicateBucket } var duplicatePayment *MPPayment @@ -1313,7 +1317,7 @@ func fetchPaymentWithSequenceNumber(tx kvdb.RTx, paymentHash lntypes.Hash, subBucket := dup.NestedReadBucket(k) if subBucket == nil { // We one bucket for each duplicate to be found. - return paymentsdb.ErrNoDuplicateNestedBucket + return ErrNoDuplicateNestedBucket } seqBytes := subBucket.Get(duplicatePaymentSequenceKey) @@ -1342,7 +1346,7 @@ func fetchPaymentWithSequenceNumber(tx kvdb.RTx, paymentHash lntypes.Hash, // failed to find the payment with this sequence number; something is // wrong. if duplicatePayment == nil { - return nil, paymentsdb.ErrDuplicateNotFound + return nil, ErrDuplicateNotFound } return duplicatePayment, nil diff --git a/channeldb/payments_kv_store_test.go b/payments/db/kv_store_test.go similarity index 93% rename from channeldb/payments_kv_store_test.go rename to payments/db/kv_store_test.go index d1953fa95d0..49e80df136a 100644 --- a/channeldb/payments_kv_store_test.go +++ b/payments/db/kv_store_test.go @@ -1,4 +1,4 @@ -package channeldb +package paymentsdb import ( "bytes" @@ -16,7 +16,6 @@ import ( "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" - paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/tlv" @@ -64,11 +63,7 @@ func genInfo(t *testing.T) (*PaymentCreationInfo, *HTLCAttemptInfo, func TestKVPaymentsDBSwitchFail(t *testing.T) { t.Parallel() - db, err := MakeTestDB(t) - require.NoError(t, err, "unable to init db") - - paymentDB, err := NewKVPaymentsDB(db) - require.NoError(t, err) + paymentDB := NewTestDB(t) info, attempt, preimg, err := genInfo(t) require.NoError(t, err, "unable to generate htlc message") @@ -195,7 +190,7 @@ func TestKVPaymentsDBSwitchFail(t *testing.T) { // Attempt a final payment, which should now fail since the prior // payment succeed. err = paymentDB.InitPayment(info.PaymentIdentifier, info) - if !errors.Is(err, paymentsdb.ErrAlreadyPaid) { + if !errors.Is(err, ErrAlreadyPaid) { t.Fatalf("unable to send htlc message: %v", err) } } @@ -205,11 +200,7 @@ func TestKVPaymentsDBSwitchFail(t *testing.T) { func TestKVPaymentsDBSwitchDoubleSend(t *testing.T) { t.Parallel() - db, err := MakeTestDB(t) - require.NoError(t, err, "unable to init db") - - paymentDB, err := NewKVPaymentsDB(db) - require.NoError(t, err) + paymentDB := NewTestDB(t) info, attempt, preimg, err := genInfo(t) require.NoError(t, err, "unable to generate htlc message") @@ -231,7 +222,7 @@ func TestKVPaymentsDBSwitchDoubleSend(t *testing.T) { // payment hash, should result in error indicating that payment has // already been sent. err = paymentDB.InitPayment(info.PaymentIdentifier, info) - require.ErrorIs(t, err, paymentsdb.ErrPaymentExists) + require.ErrorIs(t, err, ErrPaymentExists) // Record an attempt. _, err = paymentDB.RegisterAttempt(info.PaymentIdentifier, attempt) @@ -249,7 +240,7 @@ func TestKVPaymentsDBSwitchDoubleSend(t *testing.T) { // Sends base htlc message which initiate StatusInFlight. err = paymentDB.InitPayment(info.PaymentIdentifier, info) - if !errors.Is(err, paymentsdb.ErrPaymentInFlight) { + if !errors.Is(err, ErrPaymentInFlight) { t.Fatalf("payment control wrong behaviour: " + "double sending must trigger ErrPaymentInFlight error") } @@ -272,7 +263,7 @@ func TestKVPaymentsDBSwitchDoubleSend(t *testing.T) { ) err = paymentDB.InitPayment(info.PaymentIdentifier, info) - if !errors.Is(err, paymentsdb.ErrAlreadyPaid) { + if !errors.Is(err, ErrAlreadyPaid) { t.Fatalf("unable to send htlc message: %v", err) } } @@ -282,11 +273,7 @@ func TestKVPaymentsDBSwitchDoubleSend(t *testing.T) { func TestKVPaymentsDBSuccessesWithoutInFlight(t *testing.T) { t.Parallel() - db, err := MakeTestDB(t) - require.NoError(t, err, "unable to init db") - - paymentDB, err := NewKVPaymentsDB(db) - require.NoError(t, err) + paymentDB := NewTestDB(t) info, _, preimg, err := genInfo(t) require.NoError(t, err, "unable to generate htlc message") @@ -298,7 +285,7 @@ func TestKVPaymentsDBSuccessesWithoutInFlight(t *testing.T) { Preimage: preimg, }, ) - require.ErrorIs(t, err, paymentsdb.ErrPaymentNotInitiated) + require.ErrorIs(t, err, ErrPaymentNotInitiated) } // TestKVPaymentsDBFailsWithoutInFlight checks that a strict payment @@ -306,18 +293,16 @@ func TestKVPaymentsDBSuccessesWithoutInFlight(t *testing.T) { func TestKVPaymentsDBFailsWithoutInFlight(t *testing.T) { t.Parallel() - db, err := MakeTestDB(t) - require.NoError(t, err, "unable to init db") - - paymentDB, err := NewKVPaymentsDB(db) - require.NoError(t, err) + paymentDB := NewTestDB(t) info, _, _, err := genInfo(t) require.NoError(t, err, "unable to generate htlc message") // Calling Fail should return an error. - _, err = paymentDB.Fail(info.PaymentIdentifier, FailureReasonNoRoute) - require.ErrorIs(t, err, paymentsdb.ErrPaymentNotInitiated) + _, err = paymentDB.Fail( + info.PaymentIdentifier, FailureReasonNoRoute, + ) + require.ErrorIs(t, err, ErrPaymentNotInitiated) } // TestKVPaymentsDBDeleteNonInFlight checks that calling DeletePayments only @@ -325,17 +310,13 @@ func TestKVPaymentsDBFailsWithoutInFlight(t *testing.T) { func TestKVPaymentsDBDeleteNonInFlight(t *testing.T) { t.Parallel() - db, err := MakeTestDB(t) - require.NoError(t, err, "unable to init db") + paymentDB := NewTestDB(t) // Create a sequence number for duplicate payments that will not collide // with the sequence numbers for the payments we create. These values // start at 1, so 9999 is a safe bet for this test. var duplicateSeqNr = 9999 - paymentDB, err := NewKVPaymentsDB(db) - require.NoError(t, err) - payments := []struct { failed bool success bool @@ -531,7 +512,7 @@ func TestKVPaymentsDBDeleteNonInFlight(t *testing.T) { // Finally, check that we only have a single index left in the payment // index bucket. var indexCount int - err = kvdb.View(db, func(tx walletdb.ReadTx) error { + err = kvdb.View(paymentDB.db, func(tx walletdb.ReadTx) error { index := tx.ReadBucket(paymentsIndexBucket) return index.ForEach(func(k, v []byte) error { @@ -549,11 +530,7 @@ func TestKVPaymentsDBDeleteNonInFlight(t *testing.T) { func TestKVPaymentsDBDeletePayments(t *testing.T) { t.Parallel() - db, err := MakeTestDB(t) - require.NoError(t, err, "unable to init db") - - paymentDB, err := NewKVPaymentsDB(db) - require.NoError(t, err) + paymentDB := NewTestDB(t) // Register three payments: // 1. A payment with two failed attempts. @@ -611,11 +588,7 @@ func TestKVPaymentsDBDeletePayments(t *testing.T) { func TestKVPaymentsDBDeleteSinglePayment(t *testing.T) { t.Parallel() - db, err := MakeTestDB(t) - require.NoError(t, err, "unable to init db") - - paymentDB, err := NewKVPaymentsDB(db) - require.NoError(t, err) + paymentDB := NewTestDB(t) // Register four payments: // All payments will have one failed HTLC attempt and one HTLC attempt @@ -716,13 +689,7 @@ func TestKVPaymentsDBMultiShard(t *testing.T) { } runSubTest := func(t *testing.T, test testCase) { - db, err := MakeTestDB(t) - if err != nil { - t.Fatalf("unable to init db: %v", err) - } - - paymentDB, err := NewKVPaymentsDB(db) - require.NoError(t, err) + paymentDB := NewTestDB(t) info, attempt, preimg, err := genInfo(t) if err != nil { @@ -785,7 +752,7 @@ func TestKVPaymentsDBMultiShard(t *testing.T) { b := *attempt b.AttemptID = 3 _, err = paymentDB.RegisterAttempt(info.PaymentIdentifier, &b) - require.ErrorIs(t, err, paymentsdb.ErrValueExceedsAmt) + require.ErrorIs(t, err, ErrValueExceedsAmt) // Fail the second attempt. a := attempts[1] @@ -886,11 +853,11 @@ func TestKVPaymentsDBMultiShard(t *testing.T) { _, err = paymentDB.RegisterAttempt(info.PaymentIdentifier, &b) if test.settleFirst { require.ErrorIs( - t, err, paymentsdb.ErrPaymentPendingSettled, + t, err, ErrPaymentPendingSettled, ) } else { require.ErrorIs( - t, err, paymentsdb.ErrPaymentPendingFailed, + t, err, ErrPaymentPendingFailed, ) } @@ -960,21 +927,21 @@ func TestKVPaymentsDBMultiShard(t *testing.T) { // settled. case test.settleFirst && !test.settleLast: finalStatus = StatusSucceeded - registerErr = paymentsdb.ErrPaymentAlreadySucceeded + registerErr = ErrPaymentAlreadySucceeded case !test.settleFirst && test.settleLast: finalStatus = StatusSucceeded - registerErr = paymentsdb.ErrPaymentAlreadySucceeded + registerErr = ErrPaymentAlreadySucceeded // If both failed, we end up in a failed status. case !test.settleFirst && !test.settleLast: finalStatus = StatusFailed - registerErr = paymentsdb.ErrPaymentAlreadyFailed + registerErr = ErrPaymentAlreadyFailed // Otherwise, the payment has a succeed status. case test.settleFirst && test.settleLast: finalStatus = StatusSucceeded - registerErr = paymentsdb.ErrPaymentAlreadySucceeded + registerErr = ErrPaymentAlreadySucceeded } assertPaymentStatus( @@ -987,7 +954,6 @@ func TestKVPaymentsDBMultiShard(t *testing.T) { } for _, test := range tests { - test := test subTest := fmt.Sprintf("first=%v, second=%v", test.settleFirst, test.settleLast) @@ -1000,11 +966,7 @@ func TestKVPaymentsDBMultiShard(t *testing.T) { func TestKVPaymentsDBMPPRecordValidation(t *testing.T) { t.Parallel() - db, err := MakeTestDB(t) - require.NoError(t, err, "unable to init db") - - paymentDB, err := NewKVPaymentsDB(db) - require.NoError(t, err) + paymentDB := NewTestDB(t) info, attempt, _, err := genInfo(t) require.NoError(t, err, "unable to generate htlc message") @@ -1031,21 +993,21 @@ func TestKVPaymentsDBMPPRecordValidation(t *testing.T) { b.AttemptID = 1 b.Route.FinalHop().MPP = nil _, err = paymentDB.RegisterAttempt(info.PaymentIdentifier, &b) - require.ErrorIs(t, err, paymentsdb.ErrMPPayment) + require.ErrorIs(t, err, ErrMPPayment) // Try to register attempt one with a different payment address. b.Route.FinalHop().MPP = record.NewMPP( info.Value, [32]byte{2}, ) _, err = paymentDB.RegisterAttempt(info.PaymentIdentifier, &b) - require.ErrorIs(t, err, paymentsdb.ErrMPPPaymentAddrMismatch) + require.ErrorIs(t, err, ErrMPPPaymentAddrMismatch) // Try registering one with a different total amount. b.Route.FinalHop().MPP = record.NewMPP( info.Value/2, [32]byte{1}, ) _, err = paymentDB.RegisterAttempt(info.PaymentIdentifier, &b) - require.ErrorIs(t, err, paymentsdb.ErrMPPTotalAmountMismatch) + require.ErrorIs(t, err, ErrMPPTotalAmountMismatch) // Create and init a new payment. This time we'll check that we cannot // register an MPP attempt if we already registered a non-MPP one. @@ -1067,7 +1029,7 @@ func TestKVPaymentsDBMPPRecordValidation(t *testing.T) { ) _, err = paymentDB.RegisterAttempt(info.PaymentIdentifier, &b) - require.ErrorIs(t, err, paymentsdb.ErrNonMPPayment) + require.ErrorIs(t, err, ErrNonMPPayment) } // TestDeleteFailedAttempts checks that DeleteFailedAttempts properly removes @@ -1084,16 +1046,9 @@ func TestDeleteFailedAttempts(t *testing.T) { } func testDeleteFailedAttempts(t *testing.T, keepFailedPaymentAttempts bool) { - db, err := MakeTestDB(t) - require.NoError(t, err, "unable to init db") - - paymentDB, err := NewKVPaymentsDB( - db, - paymentsdb.WithKeepFailedPaymentAttempts( - keepFailedPaymentAttempts, - ), + paymentDB := NewTestDB( + t, WithKeepFailedPaymentAttempts(keepFailedPaymentAttempts), ) - require.NoError(t, err) // Register three payments: // All payments will have one failed HTLC attempt and one HTLC attempt @@ -1177,7 +1132,7 @@ func assertPaymentStatus(t *testing.T, p *KVPaymentsDB, t.Helper() payment, err := p.FetchPayment(hash) - if errors.Is(err, paymentsdb.ErrPaymentNotInitiated) { + if errors.Is(err, ErrPaymentNotInitiated) { return } if err != nil { @@ -1199,7 +1154,8 @@ type htlcStatus struct { // assertPaymentInfo retrieves the payment referred to by hash and verifies the // expected values. func assertPaymentInfo(t *testing.T, p *KVPaymentsDB, hash lntypes.Hash, - c *PaymentCreationInfo, f *FailureReason, a *htlcStatus) { + c *PaymentCreationInfo, f *FailureReason, + a *htlcStatus) { t.Helper() @@ -1277,7 +1233,7 @@ func fetchPaymentIndexEntry(_ *testing.T, p *KVPaymentsDB, indexValue := indexBucket.Get(key) if indexValue == nil { - return paymentsdb.ErrNoSequenceNrIndex + return ErrNoSequenceNrIndex } r := bytes.NewReader(indexValue) @@ -1313,7 +1269,7 @@ func assertPaymentIndex(t *testing.T, p *KVPaymentsDB, // exist. func assertNoIndex(t *testing.T, p *KVPaymentsDB, seqNr uint64) { _, err := fetchPaymentIndexEntry(t, p, seqNr) - require.Equal(t, paymentsdb.ErrNoSequenceNrIndex, err) + require.Equal(t, ErrNoSequenceNrIndex, err) } // payment is a helper structure that holds basic information on a test payment, @@ -1439,7 +1395,9 @@ func assertPayments(t *testing.T, paymentDB *KVPaymentsDB, require.Equal(t, payments, p) } -func makeFakeInfo(t *testing.T) (*PaymentCreationInfo, *HTLCAttemptInfo) { +func makeFakeInfo(t *testing.T) (*PaymentCreationInfo, + *HTLCAttemptInfo) { + var preimg lntypes.Preimage copy(preimg[:], rev[:]) @@ -1552,7 +1510,7 @@ func testSerializeRoute(t *testing.T, route route.Route) { } // deletePayment removes a payment with paymentHash from the payments database. -func deletePayment(t *testing.T, db *DB, paymentHash lntypes.Hash, +func deletePayment(t *testing.T, db kvdb.Backend, paymentHash lntypes.Hash, seqNr uint64) { t.Helper() @@ -1587,11 +1545,7 @@ func deletePayment(t *testing.T, db *DB, paymentHash lntypes.Hash, // case where a specific duplicate is not found and the duplicates bucket is not // present when we expect it to be. func TestFetchPaymentWithSequenceNumber(t *testing.T) { - db, err := MakeTestDB(t) - require.NoError(t, err) - - paymentDB, err := NewKVPaymentsDB(db) - require.NoError(t, err) + paymentDB := NewTestDB(t) // Generate a test payment which does not have duplicates. noDuplicates, _, _, err := genInfo(t) @@ -1634,12 +1588,12 @@ func TestFetchPaymentWithSequenceNumber(t *testing.T) { // Add two duplicates to our second payment. appendDuplicatePayment( - t, db, hasDuplicates.PaymentIdentifier, duplicateOneSeqNr, - preimg, + t, paymentDB.db, hasDuplicates.PaymentIdentifier, + duplicateOneSeqNr, preimg, ) appendDuplicatePayment( - t, db, hasDuplicates.PaymentIdentifier, duplicateTwoSeqNr, - preimg, + t, paymentDB.db, hasDuplicates.PaymentIdentifier, + duplicateTwoSeqNr, preimg, ) tests := []struct { @@ -1676,23 +1630,22 @@ func TestFetchPaymentWithSequenceNumber(t *testing.T) { name: "lookup non-existent duplicate", paymentHash: hasDuplicates.PaymentIdentifier, sequenceNumber: 999999, - expectedErr: paymentsdb.ErrDuplicateNotFound, + expectedErr: ErrDuplicateNotFound, }, { name: "lookup duplicate, no duplicates " + "bucket", paymentHash: noDuplicates.PaymentIdentifier, sequenceNumber: duplicateTwoSeqNr, - expectedErr: paymentsdb.ErrNoDuplicateBucket, + expectedErr: ErrNoDuplicateBucket, }, } for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { + //nolint:ll err := kvdb.Update( - db, func(tx walletdb.ReadWriteTx) error { + paymentDB.db, func(tx walletdb.ReadWriteTx) error { var seqNrBytes [8]byte byteOrder.PutUint64( seqNrBytes[:], diff --git a/payments/db/log.go b/payments/db/log.go index 08994efab89..8a77dbcec7f 100644 --- a/payments/db/log.go +++ b/payments/db/log.go @@ -8,8 +8,6 @@ import ( // log is a logger that is initialized with no output filters. This // means the package will not perform any logging by default until the caller // requests it. -// -//nolint:unused var log btclog.Logger // Subsystem defines the logging identifier for this subsystem. diff --git a/channeldb/mp_payment.go b/payments/db/payment.go similarity index 86% rename from channeldb/mp_payment.go rename to payments/db/payment.go index f75357afc0b..72338a4e50e 100644 --- a/channeldb/mp_payment.go +++ b/payments/db/payment.go @@ -1,4 +1,4 @@ -package channeldb +package paymentsdb import ( "bytes" @@ -15,10 +15,94 @@ import ( "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnwire" - paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/lightningnetwork/lnd/routing/route" ) +// FailureReason encodes the reason a payment ultimately failed. +type FailureReason byte + +const ( + // FailureReasonTimeout indicates that the payment did timeout before a + // successful payment attempt was made. + FailureReasonTimeout FailureReason = 0 + + // FailureReasonNoRoute indicates no successful route to the + // destination was found during path finding. + FailureReasonNoRoute FailureReason = 1 + + // FailureReasonError indicates that an unexpected error happened during + // payment. + FailureReasonError FailureReason = 2 + + // FailureReasonPaymentDetails indicates that either the hash is unknown + // or the final cltv delta or amount is incorrect. + FailureReasonPaymentDetails FailureReason = 3 + + // FailureReasonInsufficientBalance indicates that we didn't have enough + // balance to complete the payment. + FailureReasonInsufficientBalance FailureReason = 4 + + // FailureReasonCanceled indicates that the payment was canceled by the + // user. + FailureReasonCanceled FailureReason = 5 + + // TODO(joostjager): Add failure reasons for: + // LocalLiquidityInsufficient, RemoteCapacityInsufficient. +) + +// Error returns a human-readable error string for the FailureReason. +func (r FailureReason) Error() string { + return r.String() +} + +// String returns a human-readable FailureReason. +func (r FailureReason) String() string { + switch r { + case FailureReasonTimeout: + return "timeout" + case FailureReasonNoRoute: + return "no_route" + case FailureReasonError: + return "error" + case FailureReasonPaymentDetails: + return "incorrect_payment_details" + case FailureReasonInsufficientBalance: + return "insufficient_balance" + case FailureReasonCanceled: + return "canceled" + } + + return "unknown" +} + +// PaymentCreationInfo is the information necessary to have ready when +// initiating a payment, moving it into state InFlight. +type PaymentCreationInfo struct { + // PaymentIdentifier is the hash this payment is paying to in case of + // non-AMP payments, and the SetID for AMP payments. + PaymentIdentifier lntypes.Hash + + // Value is the amount we are paying. + Value lnwire.MilliSatoshi + + // CreationTime is the time when this payment was initiated. + CreationTime time.Time + + // PaymentRequest is the full payment request, if any. + PaymentRequest []byte + + // FirstHopCustomRecords are the TLV records that are to be sent to the + // first hop of this payment. These records will be transmitted via the + // wire message only and therefore do not affect the onion payload size. + FirstHopCustomRecords lnwire.CustomRecords +} + +// String returns a human-readable description of the payment creation info. +func (p *PaymentCreationInfo) String() string { + return fmt.Sprintf("payment_id=%v, amount=%v, created_at=%v", + p.PaymentIdentifier, p.Value, p.CreationTime) +} + // HTLCAttemptInfo contains static information about a specific HTLC attempt // for a payment. This information is used by the router to handle any errors // coming back after an attempt is made, and to query the switch about the @@ -171,8 +255,8 @@ const ( // reason. HTLCFailUnknown HTLCFailReason = 0 - // HTLCFailUnknown is recorded for htlcs that had a failure message that - // couldn't be decrypted. + // HTLCFailUnreadable is recorded for htlcs that had a failure message + // that couldn't be decrypted. HTLCFailUnreadable HTLCFailReason = 1 // HTLCFailInternal is recorded for htlcs that failed because of an @@ -350,12 +434,12 @@ func (m *MPPayment) Registrable() error { // are settled HTLCs or the payment is failed. If we already have // settled HTLCs, we won't allow adding more HTLCs. if m.State.HasSettledHTLC { - return paymentsdb.ErrPaymentPendingSettled + return ErrPaymentPendingSettled } // If the payment is already failed, we won't allow adding more HTLCs. if m.State.PaymentFailed { - return paymentsdb.ErrPaymentPendingFailed + return ErrPaymentPendingFailed } // Otherwise we can add more HTLCs. @@ -373,7 +457,7 @@ func (m *MPPayment) setState() error { totalAmt := m.Info.Value if sentAmt > totalAmt { return fmt.Errorf("%w: sent=%v, total=%v", - paymentsdb.ErrSentExceedsTotal, sentAmt, totalAmt) + ErrSentExceedsTotal, sentAmt, totalAmt) } // Get any terminal info for this payment. @@ -452,7 +536,7 @@ func (m *MPPayment) NeedWaitAttempts() (bool, error) { case StatusSucceeded: return false, fmt.Errorf("%w: parts of the payment "+ "already succeeded but still have remaining "+ - "amount %v", paymentsdb.ErrPaymentInternal, + "amount %v", ErrPaymentInternal, m.State.RemainingAmt) // The payment is failed and we have no inflight HTLCs, no need @@ -463,7 +547,7 @@ func (m *MPPayment) NeedWaitAttempts() (bool, error) { // Unknown payment status. default: return false, fmt.Errorf("%w: %s", - paymentsdb.ErrUnknownPaymentStatus, m.Status) + ErrUnknownPaymentStatus, m.Status) } } @@ -474,7 +558,7 @@ func (m *MPPayment) NeedWaitAttempts() (bool, error) { // amount, return an error. case StatusInitiated: return false, fmt.Errorf("%w: %v", - paymentsdb.ErrPaymentInternal, m.Status) + ErrPaymentInternal, m.Status) // If the payment is inflight, we must wait. // @@ -496,12 +580,12 @@ func (m *MPPayment) NeedWaitAttempts() (bool, error) { // not be zero because our sentAmt is zero. case StatusFailed: return false, fmt.Errorf("%w: %v", - paymentsdb.ErrPaymentInternal, m.Status) + ErrPaymentInternal, m.Status) // Unknown payment status. default: return false, fmt.Errorf("%w: %s", - paymentsdb.ErrUnknownPaymentStatus, m.Status) + ErrUnknownPaymentStatus, m.Status) } } @@ -510,12 +594,12 @@ func (m *MPPayment) GetState() *MPPaymentState { return m.State } -// Status returns the current status of the payment. +// GetStatus returns the current status of the payment. func (m *MPPayment) GetStatus() PaymentStatus { return m.Status } -// GetPayment returns all the HTLCs for this payment. +// GetHTLCs returns all the HTLCs for this payment. func (m *MPPayment) GetHTLCs() []HTLCAttempt { return m.HTLCs } @@ -532,7 +616,7 @@ func (m *MPPayment) AllowMoreAttempts() (bool, error) { if m.Status == StatusInitiated { return false, fmt.Errorf("%w: initiated payment has "+ "zero remainingAmt", - paymentsdb.ErrPaymentInternal) + ErrPaymentInternal) } // Otherwise, exit early since all other statuses with zero @@ -549,7 +633,7 @@ func (m *MPPayment) AllowMoreAttempts() (bool, error) { if m.Status == StatusSucceeded { return false, fmt.Errorf("%w: payment already succeeded but "+ "still have remaining amount %v", - paymentsdb.ErrPaymentInternal, m.State.RemainingAmt) + ErrPaymentInternal, m.State.RemainingAmt) } // Now check if we can register a new HTLC. @@ -654,39 +738,6 @@ func deserializeHTLCFailInfo(r io.Reader) (*HTLCFailInfo, error) { return f, nil } -// deserializeTime deserializes time as unix nanoseconds. -func deserializeTime(r io.Reader) (time.Time, error) { - var scratch [8]byte - if _, err := io.ReadFull(r, scratch[:]); err != nil { - return time.Time{}, err - } - - // Convert to time.Time. Interpret unix nano time zero as a zero - // time.Time value. - unixNano := byteOrder.Uint64(scratch[:]) - if unixNano == 0 { - return time.Time{}, nil - } - - return time.Unix(0, int64(unixNano)), nil -} - -// serializeTime serializes time as unix nanoseconds. -func serializeTime(w io.Writer, t time.Time) error { - var scratch [8]byte - - // Convert to unix nano seconds, but only if time is non-zero. Calling - // UnixNano() on a zero time yields an undefined result. - var unixNano int64 - if !t.IsZero() { - unixNano = t.UnixNano() - } - - byteOrder.PutUint64(scratch[:], uint64(unixNano)) - _, err := w.Write(scratch[:]) - return err -} - // generateSphinxPacket generates then encodes a sphinx packet which encodes // the onion route specified by the passed layer 3 route. The blob returned // from this function can immediately be included within an HTLC add packet to diff --git a/channeldb/payment_status.go b/payments/db/payment_status.go similarity index 93% rename from channeldb/payment_status.go rename to payments/db/payment_status.go index 179e22fc250..3fd01cafa36 100644 --- a/channeldb/payment_status.go +++ b/payments/db/payment_status.go @@ -1,9 +1,7 @@ -package channeldb +package paymentsdb import ( "fmt" - - paymentsdb "github.com/lightningnetwork/lnd/payments/db" ) // PaymentStatus represent current status of payment. @@ -62,24 +60,24 @@ func (ps PaymentStatus) initializable() error { // again in case other goroutines have already been creating HTLCs for // it. case StatusInitiated: - return paymentsdb.ErrPaymentExists + return ErrPaymentExists // We already have an InFlight payment on the network. We will disallow // any new payments. case StatusInFlight: - return paymentsdb.ErrPaymentInFlight + return ErrPaymentInFlight // The payment has been attempted and is succeeded so we won't allow // creating it again. case StatusSucceeded: - return paymentsdb.ErrAlreadyPaid + return ErrAlreadyPaid // We allow retrying failed payments. case StatusFailed: return nil default: - return fmt.Errorf("%w: %v", paymentsdb.ErrUnknownPaymentStatus, + return fmt.Errorf("%w: %v", ErrUnknownPaymentStatus, ps) } } @@ -96,7 +94,7 @@ func (ps PaymentStatus) removable() error { // There are still inflight HTLCs and the payment needs to wait for the // final outcomes. case StatusInFlight: - return paymentsdb.ErrPaymentInFlight + return ErrPaymentInFlight // The payment has been attempted and is succeeded and is allowed to be // removed. @@ -108,7 +106,7 @@ func (ps PaymentStatus) removable() error { return nil default: - return fmt.Errorf("%w: %v", paymentsdb.ErrUnknownPaymentStatus, + return fmt.Errorf("%w: %v", ErrUnknownPaymentStatus, ps) } } @@ -127,13 +125,13 @@ func (ps PaymentStatus) updatable() error { // If the payment has a terminal condition, we won't allow any updates. case StatusSucceeded: - return paymentsdb.ErrPaymentAlreadySucceeded + return ErrPaymentAlreadySucceeded case StatusFailed: - return paymentsdb.ErrPaymentAlreadyFailed + return ErrPaymentAlreadyFailed default: - return fmt.Errorf("%w: %v", paymentsdb.ErrUnknownPaymentStatus, + return fmt.Errorf("%w: %v", ErrUnknownPaymentStatus, ps) } } diff --git a/channeldb/payment_status_test.go b/payments/db/payment_status_test.go similarity index 92% rename from channeldb/payment_status_test.go rename to payments/db/payment_status_test.go index bd6181870c4..1bb4dc3889a 100644 --- a/channeldb/payment_status_test.go +++ b/payments/db/payment_status_test.go @@ -1,11 +1,10 @@ -package channeldb +package paymentsdb import ( "fmt" "testing" "github.com/lightningnetwork/lnd/lntypes" - paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/stretchr/testify/require" ) @@ -198,33 +197,33 @@ func TestPaymentStatusActions(t *testing.T) { }{ { status: StatusInitiated, - initErr: paymentsdb.ErrPaymentExists, + initErr: ErrPaymentExists, updateErr: nil, removeErr: nil, }, { status: StatusInFlight, - initErr: paymentsdb.ErrPaymentInFlight, + initErr: ErrPaymentInFlight, updateErr: nil, - removeErr: paymentsdb.ErrPaymentInFlight, + removeErr: ErrPaymentInFlight, }, { status: StatusSucceeded, - initErr: paymentsdb.ErrAlreadyPaid, - updateErr: paymentsdb.ErrPaymentAlreadySucceeded, + initErr: ErrAlreadyPaid, + updateErr: ErrPaymentAlreadySucceeded, removeErr: nil, }, { status: StatusFailed, initErr: nil, - updateErr: paymentsdb.ErrPaymentAlreadyFailed, + updateErr: ErrPaymentAlreadyFailed, removeErr: nil, }, { status: 0, - initErr: paymentsdb.ErrUnknownPaymentStatus, - updateErr: paymentsdb.ErrUnknownPaymentStatus, - removeErr: paymentsdb.ErrUnknownPaymentStatus, + initErr: ErrUnknownPaymentStatus, + updateErr: ErrUnknownPaymentStatus, + removeErr: ErrUnknownPaymentStatus, }, } diff --git a/channeldb/mp_payment_test.go b/payments/db/payment_test.go similarity index 55% rename from channeldb/mp_payment_test.go rename to payments/db/payment_test.go index 455a04de0a8..9889f9d88ed 100644 --- a/channeldb/mp_payment_test.go +++ b/payments/db/payment_test.go @@ -1,14 +1,20 @@ -package channeldb +package paymentsdb import ( "bytes" + "context" "fmt" + "math" + "reflect" "testing" + "time" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" - paymentsdb "github.com/lightningnetwork/lnd/payments/db" + "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/routing/route" "github.com/stretchr/testify/require" ) @@ -20,8 +26,459 @@ var ( 0x4f, 0x2f, 0x6f, 0x25, 0x88, 0xa3, 0xef, 0xb9, 0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53, } + + rev = [chainhash.HashSize]byte{ + 0x51, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda, + 0x48, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17, + 0x2d, 0xe7, 0x93, 0xe4, + } +) + +var ( + priv, _ = btcec.NewPrivateKey() + pub = priv.PubKey() + vertex = route.NewVertex(pub) + + testHop1 = &route.Hop{ + PubKeyBytes: vertex, + ChannelID: 12345, + OutgoingTimeLock: 111, + AmtToForward: 555, + CustomRecords: record.CustomSet{ + 65536: []byte{}, + 80001: []byte{}, + }, + MPP: record.NewMPP(32, [32]byte{0x42}), + Metadata: []byte{1, 2, 3}, + } + + testHop2 = &route.Hop{ + PubKeyBytes: vertex, + ChannelID: 12345, + OutgoingTimeLock: 111, + AmtToForward: 555, + LegacyPayload: true, + } + + testRoute = route.Route{ + TotalTimeLock: 123, + TotalAmount: 1234567, + SourcePubKey: vertex, + Hops: []*route.Hop{ + testHop2, + testHop1, + }, + } + + testBlindedRoute = route.Route{ + TotalTimeLock: 150, + TotalAmount: 1000, + SourcePubKey: vertex, + Hops: []*route.Hop{ + { + PubKeyBytes: vertex, + ChannelID: 9876, + OutgoingTimeLock: 120, + AmtToForward: 900, + EncryptedData: []byte{1, 3, 3}, + BlindingPoint: pub, + }, + { + PubKeyBytes: vertex, + EncryptedData: []byte{3, 2, 1}, + }, + { + PubKeyBytes: vertex, + Metadata: []byte{4, 5, 6}, + AmtToForward: 500, + OutgoingTimeLock: 100, + TotalAmtMsat: 500, + }, + }, + } ) +// assertRouteEquals compares to routes for equality and returns an error if +// they are not equal. +func assertRouteEqual(a, b *route.Route) error { + if !reflect.DeepEqual(a, b) { + return fmt.Errorf("HTLCAttemptInfos don't match: %v vs %v", + spew.Sdump(a), spew.Sdump(b)) + } + + return nil +} + +// TestQueryPayments tests retrieval of payments with forwards and reversed +// queries. +func TestQueryPayments(t *testing.T) { + // Define table driven test for QueryPayments. + // Test payments have sequence indices [1, 3, 4, 5, 6, 7]. + // Note that the payment with index 7 has the same payment hash as 6, + // and is stored in a nested bucket within payment 6 rather than being + // its own entry in the payments bucket. We do this to test retrieval + // of legacy payments. + tests := []struct { + name string + query Query + firstIndex uint64 + lastIndex uint64 + + // expectedSeqNrs contains the set of sequence numbers we expect + // our query to return. + expectedSeqNrs []uint64 + }{ + { + name: "IndexOffset at the end of the payments range", + query: Query{ + IndexOffset: 7, + MaxPayments: 7, + Reversed: false, + IncludeIncomplete: true, + }, + firstIndex: 0, + lastIndex: 0, + expectedSeqNrs: nil, + }, + { + name: "query in forwards order, start at beginning", + query: Query{ + IndexOffset: 0, + MaxPayments: 2, + Reversed: false, + IncludeIncomplete: true, + }, + firstIndex: 1, + lastIndex: 3, + expectedSeqNrs: []uint64{1, 3}, + }, + { + name: "query in forwards order, start at end, overflow", + query: Query{ + IndexOffset: 6, + MaxPayments: 2, + Reversed: false, + IncludeIncomplete: true, + }, + firstIndex: 7, + lastIndex: 7, + expectedSeqNrs: []uint64{7}, + }, + { + name: "start at offset index outside of payments", + query: Query{ + IndexOffset: 20, + MaxPayments: 2, + Reversed: false, + IncludeIncomplete: true, + }, + firstIndex: 0, + lastIndex: 0, + expectedSeqNrs: nil, + }, + { + name: "overflow in forwards order", + query: Query{ + IndexOffset: 4, + MaxPayments: math.MaxUint64, + Reversed: false, + IncludeIncomplete: true, + }, + firstIndex: 5, + lastIndex: 7, + expectedSeqNrs: []uint64{5, 6, 7}, + }, + { + name: "start at offset index outside of payments, " + + "reversed order", + query: Query{ + IndexOffset: 9, + MaxPayments: 2, + Reversed: true, + IncludeIncomplete: true, + }, + firstIndex: 6, + lastIndex: 7, + expectedSeqNrs: []uint64{6, 7}, + }, + { + name: "query in reverse order, start at end", + query: Query{ + IndexOffset: 0, + MaxPayments: 2, + Reversed: true, + IncludeIncomplete: true, + }, + firstIndex: 6, + lastIndex: 7, + expectedSeqNrs: []uint64{6, 7}, + }, + { + name: "query in reverse order, starting in middle", + query: Query{ + IndexOffset: 4, + MaxPayments: 2, + Reversed: true, + IncludeIncomplete: true, + }, + firstIndex: 1, + lastIndex: 3, + expectedSeqNrs: []uint64{1, 3}, + }, + { + name: "query in reverse order, starting in middle, " + + "with underflow", + query: Query{ + IndexOffset: 4, + MaxPayments: 5, + Reversed: true, + IncludeIncomplete: true, + }, + firstIndex: 1, + lastIndex: 3, + expectedSeqNrs: []uint64{1, 3}, + }, + { + name: "all payments in reverse, order maintained", + query: Query{ + IndexOffset: 0, + MaxPayments: 7, + Reversed: true, + IncludeIncomplete: true, + }, + firstIndex: 1, + lastIndex: 7, + expectedSeqNrs: []uint64{1, 3, 4, 5, 6, 7}, + }, + { + name: "exclude incomplete payments", + query: Query{ + IndexOffset: 0, + MaxPayments: 7, + Reversed: false, + IncludeIncomplete: false, + }, + firstIndex: 7, + lastIndex: 7, + expectedSeqNrs: []uint64{7}, + }, + { + name: "query payments at index gap", + query: Query{ + IndexOffset: 1, + MaxPayments: 7, + Reversed: false, + IncludeIncomplete: true, + }, + firstIndex: 3, + lastIndex: 7, + expectedSeqNrs: []uint64{3, 4, 5, 6, 7}, + }, + { + name: "query payments reverse before index gap", + query: Query{ + IndexOffset: 3, + MaxPayments: 7, + Reversed: true, + IncludeIncomplete: true, + }, + firstIndex: 1, + lastIndex: 1, + expectedSeqNrs: []uint64{1}, + }, + { + name: "query payments reverse on index gap", + query: Query{ + IndexOffset: 2, + MaxPayments: 7, + Reversed: true, + IncludeIncomplete: true, + }, + firstIndex: 1, + lastIndex: 1, + expectedSeqNrs: []uint64{1}, + }, + { + name: "query payments forward on index gap", + query: Query{ + IndexOffset: 2, + MaxPayments: 2, + Reversed: false, + IncludeIncomplete: true, + }, + firstIndex: 3, + lastIndex: 4, + expectedSeqNrs: []uint64{3, 4}, + }, + { + name: "query in forwards order, with start creation " + + "time", + query: Query{ + IndexOffset: 0, + MaxPayments: 2, + Reversed: false, + IncludeIncomplete: true, + CreationDateStart: 5, + }, + firstIndex: 5, + lastIndex: 6, + expectedSeqNrs: []uint64{5, 6}, + }, + { + name: "query in forwards order, with start creation " + + "time at end, overflow", + query: Query{ + IndexOffset: 0, + MaxPayments: 2, + Reversed: false, + IncludeIncomplete: true, + CreationDateStart: 7, + }, + firstIndex: 7, + lastIndex: 7, + expectedSeqNrs: []uint64{7}, + }, + { + name: "query with start and end creation time", + query: Query{ + IndexOffset: 9, + MaxPayments: math.MaxUint64, + Reversed: true, + IncludeIncomplete: true, + CreationDateStart: 3, + CreationDateEnd: 5, + }, + firstIndex: 3, + lastIndex: 5, + expectedSeqNrs: []uint64{3, 4, 5}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + paymentDB := NewTestDB(t) + + // Initialize the payment database. + paymentDB, err := NewKVPaymentsDB(paymentDB.db) + require.NoError(t, err) + + // Make a preliminary query to make sure it's ok to + // query when we have no payments. + resp, err := paymentDB.QueryPayments(ctx, tt.query) + require.NoError(t, err) + require.Len(t, resp.Payments, 0) + + // Populate the database with a set of test payments. + // We create 6 original payments, deleting the payment + // at index 2 so that we cover the case where sequence + // numbers are missing. We also add a duplicate payment + // to the last payment added to test the legacy case + // where we have duplicates in the nested duplicates + // bucket. + nonDuplicatePayments := 6 + + for i := 0; i < nonDuplicatePayments; i++ { + // Generate a test payment. + info, _, preimg, err := genInfo(t) + if err != nil { + t.Fatalf("unable to create test "+ + "payment: %v", err) + } + // Override creation time to allow for testing + // of CreationDateStart and CreationDateEnd. + info.CreationTime = time.Unix(int64(i+1), 0) + + // Create a new payment entry in the database. + err = paymentDB.InitPayment( + info.PaymentIdentifier, info, + ) + require.NoError(t, err) + + // Immediately delete the payment with index 2. + if i == 1 { + pmt, err := paymentDB.FetchPayment( + info.PaymentIdentifier, + ) + require.NoError(t, err) + + deletePayment( + t, paymentDB.db, + info.PaymentIdentifier, + pmt.SequenceNum, + ) + } + + // If we are on the last payment entry, add a + // duplicate payment with sequence number equal + // to the parent payment + 1. Note that + // duplicate payments will always be succeeded. + if i == (nonDuplicatePayments - 1) { + pmt, err := paymentDB.FetchPayment( + info.PaymentIdentifier, + ) + require.NoError(t, err) + + appendDuplicatePayment( + t, paymentDB.db, + info.PaymentIdentifier, + pmt.SequenceNum+1, + preimg, + ) + } + } + + // Fetch all payments in the database. + allPayments, err := paymentDB.FetchPayments() + if err != nil { + t.Fatalf("payments could not be fetched from "+ + "database: %v", err) + } + + if len(allPayments) != 6 { + t.Fatalf("Number of payments received does "+ + "not match expected one. Got %v, "+ + "want %v.", len(allPayments), 6) + } + + querySlice, err := paymentDB.QueryPayments( + ctx, tt.query, + ) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if tt.firstIndex != querySlice.FirstIndexOffset || + tt.lastIndex != querySlice.LastIndexOffset { + + t.Errorf("First or last index does not match "+ + "expected index. Want (%d, %d), "+ + "got (%d, %d).", + tt.firstIndex, tt.lastIndex, + querySlice.FirstIndexOffset, + querySlice.LastIndexOffset) + } + + if len(querySlice.Payments) != len(tt.expectedSeqNrs) { + t.Errorf("expected: %v payments, got: %v", + len(tt.expectedSeqNrs), + len(querySlice.Payments)) + } + + for i, seqNr := range tt.expectedSeqNrs { + q := querySlice.Payments[i] + if seqNr != q.SequenceNum { + t.Errorf("sequence numbers do not "+ + "match, got %v, want %v", + q.SequenceNum, seqNr) + } + } + }) + } +} + // TestLazySessionKeyDeserialize tests that we can read htlc attempt session // keys that were previously serialized as a private key as raw bytes. func TestLazySessionKeyDeserialize(t *testing.T) { @@ -66,35 +523,35 @@ func TestRegistrable(t *testing.T) { // Test inflight status with settled HTLC but no failed // payment. status: StatusInFlight, - registryErr: paymentsdb.ErrPaymentPendingSettled, + registryErr: ErrPaymentPendingSettled, hasSettledHTLC: true, }, { // Test inflight status with no settled HTLC but failed // payment. status: StatusInFlight, - registryErr: paymentsdb.ErrPaymentPendingFailed, + registryErr: ErrPaymentPendingFailed, paymentFailed: true, }, { // Test error state with settled HTLC and failed // payment. status: 0, - registryErr: paymentsdb.ErrUnknownPaymentStatus, + registryErr: ErrUnknownPaymentStatus, hasSettledHTLC: true, paymentFailed: true, }, { status: StatusSucceeded, - registryErr: paymentsdb.ErrPaymentAlreadySucceeded, + registryErr: ErrPaymentAlreadySucceeded, }, { status: StatusFailed, - registryErr: paymentsdb.ErrPaymentAlreadyFailed, + registryErr: ErrPaymentAlreadyFailed, }, { status: 0, - registryErr: paymentsdb.ErrUnknownPaymentStatus, + registryErr: ErrUnknownPaymentStatus, }, } @@ -150,7 +607,7 @@ func TestPaymentSetState(t *testing.T) { }, }, totalAmt: 1, - errExpected: paymentsdb.ErrSentExceedsTotal, + errExpected: ErrSentExceedsTotal, }, { // Test that when the htlc is failed, the fee is not @@ -295,7 +752,7 @@ func TestNeedWaitAttempts(t *testing.T) { status: StatusSucceeded, remainingAmt: 1000, needWait: false, - expectedErr: paymentsdb.ErrPaymentInternal, + expectedErr: ErrPaymentInternal, }, { // Payment is in terminal state, no need to wait. @@ -310,7 +767,7 @@ func TestNeedWaitAttempts(t *testing.T) { status: StatusInitiated, remainingAmt: 0, needWait: false, - expectedErr: paymentsdb.ErrPaymentInternal, + expectedErr: ErrPaymentInternal, }, { // With zero remainingAmt we must wait for the results. @@ -331,21 +788,21 @@ func TestNeedWaitAttempts(t *testing.T) { status: StatusFailed, remainingAmt: 0, needWait: false, - expectedErr: paymentsdb.ErrPaymentInternal, + expectedErr: ErrPaymentInternal, }, { // Payment is in an unknown status, return an error. status: 0, remainingAmt: 0, needWait: false, - expectedErr: paymentsdb.ErrUnknownPaymentStatus, + expectedErr: ErrUnknownPaymentStatus, }, { // Payment is in an unknown status, return an error. status: 0, remainingAmt: 1000, needWait: false, - expectedErr: paymentsdb.ErrUnknownPaymentStatus, + expectedErr: ErrUnknownPaymentStatus, }, } @@ -398,7 +855,7 @@ func TestAllowMoreAttempts(t *testing.T) { status: StatusInitiated, remainingAmt: 0, allowMore: false, - expectedErr: paymentsdb.ErrPaymentInternal, + expectedErr: ErrPaymentInternal, }, { // With zero remainingAmt we don't allow more HTLC @@ -506,7 +963,7 @@ func TestAllowMoreAttempts(t *testing.T) { remainingAmt: 1000, hasSettledHTLC: true, allowMore: false, - expectedErr: paymentsdb.ErrPaymentInternal, + expectedErr: ErrPaymentInternal, }, { // With the payment failed with no inflight HTLCs, we diff --git a/payments/db/query.go b/payments/db/query.go new file mode 100644 index 00000000000..5904ab56a4f --- /dev/null +++ b/payments/db/query.go @@ -0,0 +1,69 @@ +package paymentsdb + +// Query represents a query to the payments database starting or ending +// at a certain offset index. The number of retrieved records can be limited. +type Query struct { + // IndexOffset determines the starting point of the payments query and + // is always exclusive. In normal order, the query starts at the next + // higher (available) index compared to IndexOffset. In reversed order, + // the query ends at the next lower (available) index compared to the + // IndexOffset. In the case of a zero index_offset, the query will start + // with the oldest payment when paginating forwards, or will end with + // the most recent payment when paginating backwards. + IndexOffset uint64 + + // MaxPayments is the maximal number of payments returned in the + // payments query. + MaxPayments uint64 + + // Reversed gives a meaning to the IndexOffset. If reversed is set to + // true, the query will fetch payments with indices lower than the + // IndexOffset, otherwise, it will return payments with indices greater + // than the IndexOffset. + Reversed bool + + // If IncludeIncomplete is true, then return payments that have not yet + // fully completed. This means that pending payments, as well as failed + // payments will show up if this field is set to true. + IncludeIncomplete bool + + // CountTotal indicates that all payments currently present in the + // payment index (complete and incomplete) should be counted. + CountTotal bool + + // CreationDateStart, expressed in Unix seconds, if set, filters out + // all payments with a creation date greater than or equal to it. + CreationDateStart int64 + + // CreationDateEnd, expressed in Unix seconds, if set, filters out all + // payments with a creation date less than or equal to it. + CreationDateEnd int64 +} + +// Response contains the result of a query to the payments database. +// It includes the set of payments that match the query and integers which +// represent the index of the first and last item returned in the series of +// payments. These integers allow callers to resume their query in the event +// that the query's response exceeds the max number of returnable events. +type Response struct { + // Payments is the set of payments returned from the database for the + // Query. + Payments []*MPPayment + + // FirstIndexOffset is the index of the first element in the set of + // returned MPPayments. Callers can use this to resume their query + // in the event that the slice has too many events to fit into a single + // response. The offset can be used to continue reverse pagination. + FirstIndexOffset uint64 + + // LastIndexOffset is the index of the last element in the set of + // returned MPPayments. Callers can use this to resume their query + // in the event that the slice has too many events to fit into a single + // response. The offset can be used to continue forward pagination. + LastIndexOffset uint64 + + // TotalCount represents the total number of payments that are currently + // stored in the payment database. This will only be set if the + // CountTotal field in the query was set to true. + TotalCount uint64 +} diff --git a/payments/db/setup_test.go b/payments/db/setup_test.go new file mode 100644 index 00000000000..903eeca5ea4 --- /dev/null +++ b/payments/db/setup_test.go @@ -0,0 +1,11 @@ +package paymentsdb + +import ( + "testing" + + "github.com/lightningnetwork/lnd/kvdb" +) + +func TestMain(m *testing.M) { + kvdb.RunTests(m) +} diff --git a/payments/db/test_kvdb.go b/payments/db/test_kvdb.go new file mode 100644 index 00000000000..25e236e48d4 --- /dev/null +++ b/payments/db/test_kvdb.go @@ -0,0 +1,23 @@ +package paymentsdb + +import ( + "testing" + + "github.com/lightningnetwork/lnd/kvdb" + "github.com/stretchr/testify/require" +) + +// NewTestDB is a helper function that creates an BBolt database for testing. +func NewTestDB(t *testing.T, opts ...OptionModifier) *KVPaymentsDB { + backend, backendCleanup, err := kvdb.GetTestBackend( + t.TempDir(), "kvPaymentDB", + ) + require.NoError(t, err) + + t.Cleanup(backendCleanup) + + paymentDB, err := NewKVPaymentsDB(backend, opts...) + require.NoError(t, err) + + return paymentDB +} diff --git a/routing/control_tower.go b/routing/control_tower.go index b2e4b7bd518..c5af230f7ac 100644 --- a/routing/control_tower.go +++ b/routing/control_tower.go @@ -3,9 +3,9 @@ package routing import ( "sync" - "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/multimutex" + paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/lightningnetwork/lnd/queue" ) @@ -13,23 +13,23 @@ import ( // the payment lifecycle. type DBMPPayment interface { // GetState returns the current state of the payment. - GetState() *channeldb.MPPaymentState + GetState() *paymentsdb.MPPaymentState // Terminated returns true if the payment is in a final state. Terminated() bool // GetStatus returns the current status of the payment. - GetStatus() channeldb.PaymentStatus + GetStatus() paymentsdb.PaymentStatus // NeedWaitAttempts specifies whether the payment needs to wait for the // outcome of an attempt. NeedWaitAttempts() (bool, error) // GetHTLCs returns all HTLCs of this payment. - GetHTLCs() []channeldb.HTLCAttempt + GetHTLCs() []paymentsdb.HTLCAttempt // InFlightHTLCs returns all HTLCs that are in flight. - InFlightHTLCs() []channeldb.HTLCAttempt + InFlightHTLCs() []paymentsdb.HTLCAttempt // AllowMoreAttempts is used to decide whether we can safely attempt // more HTLCs for a given payment state. Return an error if the payment @@ -38,7 +38,7 @@ type DBMPPayment interface { // TerminalInfo returns the settled HTLC attempt or the payment's // failure reason. - TerminalInfo() (*channeldb.HTLCAttempt, *channeldb.FailureReason) + TerminalInfo() (*paymentsdb.HTLCAttempt, *paymentsdb.FailureReason) } // ControlTower tracks all outgoing payments made, whose primary purpose is to @@ -49,7 +49,7 @@ type DBMPPayment interface { type ControlTower interface { // This method checks that no succeeded payment exist for this payment // hash. - InitPayment(lntypes.Hash, *channeldb.PaymentCreationInfo) error + InitPayment(lntypes.Hash, *paymentsdb.PaymentCreationInfo) error // DeleteFailedAttempts removes all failed HTLCs from the db. It should // be called for a given payment whenever all inflight htlcs are @@ -57,7 +57,7 @@ type ControlTower interface { DeleteFailedAttempts(lntypes.Hash) error // RegisterAttempt atomically records the provided HTLCAttemptInfo. - RegisterAttempt(lntypes.Hash, *channeldb.HTLCAttemptInfo) error + RegisterAttempt(lntypes.Hash, *paymentsdb.HTLCAttemptInfo) error // SettleAttempt marks the given attempt settled with the preimage. If // this is a multi shard payment, this might implicitly mean the the @@ -67,12 +67,12 @@ type ControlTower interface { // error to prevent us from making duplicate payments to the same // payment hash. The provided preimage is atomically saved to the DB // for record keeping. - SettleAttempt(lntypes.Hash, uint64, *channeldb.HTLCSettleInfo) ( - *channeldb.HTLCAttempt, error) + SettleAttempt(lntypes.Hash, uint64, *paymentsdb.HTLCSettleInfo) ( + *paymentsdb.HTLCAttempt, error) // FailAttempt marks the given payment attempt failed. - FailAttempt(lntypes.Hash, uint64, *channeldb.HTLCFailInfo) ( - *channeldb.HTLCAttempt, error) + FailAttempt(lntypes.Hash, uint64, *paymentsdb.HTLCFailInfo) ( + *paymentsdb.HTLCAttempt, error) // FetchPayment fetches the payment corresponding to the given payment // hash. @@ -84,10 +84,10 @@ type ControlTower interface { // invoking this method, InitPayment should return nil on its next call // for this payment hash, allowing the user to make a subsequent // payment. - FailPayment(lntypes.Hash, channeldb.FailureReason) error + FailPayment(lntypes.Hash, paymentsdb.FailureReason) error // FetchInFlightPayments returns all payments with status InFlight. - FetchInFlightPayments() ([]*channeldb.MPPayment, error) + FetchInFlightPayments() ([]*paymentsdb.MPPayment, error) // SubscribePayment subscribes to updates for the payment with the given // hash. A first update with the current state of the payment is always @@ -151,7 +151,7 @@ func (s *controlTowerSubscriberImpl) Updates() <-chan interface{} { // controlTower is persistent implementation of ControlTower to restrict // double payment sending. type controlTower struct { - db *channeldb.KVPaymentsDB + db *paymentsdb.KVPaymentsDB // subscriberIndex is used to provide a unique id for each subscriber // to all payments. This is used to easily remove the subscriber when @@ -168,7 +168,7 @@ type controlTower struct { } // NewControlTower creates a new instance of the controlTower. -func NewControlTower(db *channeldb.KVPaymentsDB) ControlTower { +func NewControlTower(db *paymentsdb.KVPaymentsDB) ControlTower { return &controlTower{ db: db, subscribersAllPayments: make( @@ -184,7 +184,7 @@ func NewControlTower(db *channeldb.KVPaymentsDB) ControlTower { // method returns successfully, the payment is guaranteed to be in the // Initiated state. func (p *controlTower) InitPayment(paymentHash lntypes.Hash, - info *channeldb.PaymentCreationInfo) error { + info *paymentsdb.PaymentCreationInfo) error { err := p.db.InitPayment(paymentHash, info) if err != nil { @@ -215,7 +215,7 @@ func (p *controlTower) DeleteFailedAttempts(paymentHash lntypes.Hash) error { // RegisterAttempt atomically records the provided HTLCAttemptInfo to the // DB. func (p *controlTower) RegisterAttempt(paymentHash lntypes.Hash, - attempt *channeldb.HTLCAttemptInfo) error { + attempt *paymentsdb.HTLCAttemptInfo) error { p.paymentsMtx.Lock(paymentHash) defer p.paymentsMtx.Unlock(paymentHash) @@ -235,8 +235,8 @@ func (p *controlTower) RegisterAttempt(paymentHash lntypes.Hash, // this is a multi shard payment, this might implicitly mean the the // full payment succeeded. func (p *controlTower) SettleAttempt(paymentHash lntypes.Hash, - attemptID uint64, settleInfo *channeldb.HTLCSettleInfo) ( - *channeldb.HTLCAttempt, error) { + attemptID uint64, settleInfo *paymentsdb.HTLCSettleInfo) ( + *paymentsdb.HTLCAttempt, error) { p.paymentsMtx.Lock(paymentHash) defer p.paymentsMtx.Unlock(paymentHash) @@ -254,8 +254,8 @@ func (p *controlTower) SettleAttempt(paymentHash lntypes.Hash, // FailAttempt marks the given payment attempt failed. func (p *controlTower) FailAttempt(paymentHash lntypes.Hash, - attemptID uint64, failInfo *channeldb.HTLCFailInfo) ( - *channeldb.HTLCAttempt, error) { + attemptID uint64, failInfo *paymentsdb.HTLCFailInfo) ( + *paymentsdb.HTLCAttempt, error) { p.paymentsMtx.Lock(paymentHash) defer p.paymentsMtx.Unlock(paymentHash) @@ -286,7 +286,7 @@ func (p *controlTower) FetchPayment(paymentHash lntypes.Hash) ( // NOTE: This method will overwrite the failure reason if the payment is already // failed. func (p *controlTower) FailPayment(paymentHash lntypes.Hash, - reason channeldb.FailureReason) error { + reason paymentsdb.FailureReason) error { p.paymentsMtx.Lock(paymentHash) defer p.paymentsMtx.Unlock(paymentHash) @@ -303,7 +303,9 @@ func (p *controlTower) FailPayment(paymentHash lntypes.Hash, } // FetchInFlightPayments returns all payments with status InFlight. -func (p *controlTower) FetchInFlightPayments() ([]*channeldb.MPPayment, error) { +func (p *controlTower) FetchInFlightPayments() ([]*paymentsdb.MPPayment, + error) { + return p.db.FetchInFlightPayments() } @@ -386,7 +388,7 @@ func (p *controlTower) SubscribeAllPayments() (ControlTowerSubscriber, error) { // be executed atomically (by means of a lock) with the database update to // guarantee consistency of the notifications. func (p *controlTower) notifySubscribers(paymentHash lntypes.Hash, - event *channeldb.MPPayment) { + event *paymentsdb.MPPayment) { // Get all subscribers for this payment. p.subscribersMtx.Lock() diff --git a/routing/control_tower_test.go b/routing/control_tower_test.go index f45c2de385f..3e01065962d 100644 --- a/routing/control_tower_test.go +++ b/routing/control_tower_test.go @@ -50,7 +50,7 @@ func TestControlTowerSubscribeUnknown(t *testing.T) { db := initDB(t) - paymentDB, err := channeldb.NewKVPaymentsDB( + paymentDB, err := paymentsdb.NewKVPaymentsDB( db, paymentsdb.WithKeepFailedPaymentAttempts(true), ) @@ -70,7 +70,7 @@ func TestControlTowerSubscribeSuccess(t *testing.T) { db := initDB(t) - paymentDB, err := channeldb.NewKVPaymentsDB(db) + paymentDB, err := paymentsdb.NewKVPaymentsDB(db) require.NoError(t, err) pControl := NewControlTower(paymentDB) @@ -102,7 +102,7 @@ func TestControlTowerSubscribeSuccess(t *testing.T) { require.NoError(t, err, "expected subscribe to succeed, but got") // Mark the payment as successful. - settleInfo := channeldb.HTLCSettleInfo{ + settleInfo := paymentsdb.HTLCSettleInfo{ Preimage: preimg, } htlcAttempt, err := pControl.SettleAttempt( @@ -126,19 +126,27 @@ func TestControlTowerSubscribeSuccess(t *testing.T) { } for i, s := range subscribers { - var result *channeldb.MPPayment + var result *paymentsdb.MPPayment for result == nil || !result.Terminated() { select { case item := <-s.Updates(): - result = item.(*channeldb.MPPayment) + payment, ok := item.(*paymentsdb.MPPayment) + require.True( + t, ok, "unexpected payment type: %T", + item) + + result = payment + case <-time.After(testTimeout): t.Fatal("timeout waiting for payment result") } } - require.Equalf(t, channeldb.StatusSucceeded, result.GetStatus(), - "subscriber %v failed, want %s, got %s", i, - channeldb.StatusSucceeded, result.GetStatus()) + require.Equalf(t, paymentsdb.StatusSucceeded, + result.GetStatus(), "subscriber %v failed, want %s, "+ + "got %s", i, paymentsdb.StatusSucceeded, + result.GetStatus(), + ) attempt, _ := result.TerminalInfo() if attempt.Settle.Preimage != preimg { @@ -192,7 +200,7 @@ func TestKVPaymentsDBSubscribeAllSuccess(t *testing.T) { db := initDB(t) - paymentDB, err := channeldb.NewKVPaymentsDB( + paymentDB, err := paymentsdb.NewKVPaymentsDB( db, paymentsdb.WithKeepFailedPaymentAttempts(true), ) @@ -228,7 +236,7 @@ func TestKVPaymentsDBSubscribeAllSuccess(t *testing.T) { require.NoError(t, err) // Mark the first payment as successful. - settleInfo1 := channeldb.HTLCSettleInfo{ + settleInfo1 := paymentsdb.HTLCSettleInfo{ Preimage: preimg1, } htlcAttempt1, err := pControl.SettleAttempt( @@ -241,7 +249,7 @@ func TestKVPaymentsDBSubscribeAllSuccess(t *testing.T) { ) // Mark the second payment as successful. - settleInfo2 := channeldb.HTLCSettleInfo{ + settleInfo2 := paymentsdb.HTLCSettleInfo{ Preimage: preimg2, } htlcAttempt2, err := pControl.SettleAttempt( @@ -255,14 +263,20 @@ func TestKVPaymentsDBSubscribeAllSuccess(t *testing.T) { // The two payments will be asserted individually, store the last update // for each payment. - results := make(map[lntypes.Hash]*channeldb.MPPayment) + results := make(map[lntypes.Hash]*paymentsdb.MPPayment) // After exactly 6 updates both payments will/should have completed. for i := 0; i < 6; i++ { select { case item := <-subscription.Updates(): - id := item.(*channeldb.MPPayment).Info.PaymentIdentifier - results[id] = item.(*channeldb.MPPayment) + payment, ok := item.(*paymentsdb.MPPayment) + require.True( + t, ok, "unexpected payment type: %T", + item) + + id := payment.Info.PaymentIdentifier + results[id] = payment + case <-time.After(testTimeout): require.Fail(t, "timeout waiting for payment result") } @@ -270,7 +284,7 @@ func TestKVPaymentsDBSubscribeAllSuccess(t *testing.T) { result1 := results[info1.PaymentIdentifier] require.Equal( - t, channeldb.StatusSucceeded, result1.GetStatus(), + t, paymentsdb.StatusSucceeded, result1.GetStatus(), "unexpected payment state payment 1", ) @@ -288,7 +302,7 @@ func TestKVPaymentsDBSubscribeAllSuccess(t *testing.T) { result2 := results[info2.PaymentIdentifier] require.Equal( - t, channeldb.StatusSucceeded, result2.GetStatus(), + t, paymentsdb.StatusSucceeded, result2.GetStatus(), "unexpected payment state payment 2", ) @@ -311,7 +325,7 @@ func TestKVPaymentsDBSubscribeAllImmediate(t *testing.T) { db := initDB(t) - paymentDB, err := channeldb.NewKVPaymentsDB( + paymentDB, err := paymentsdb.NewKVPaymentsDB( db, paymentsdb.WithKeepFailedPaymentAttempts(true), ) @@ -337,11 +351,17 @@ func TestKVPaymentsDBSubscribeAllImmediate(t *testing.T) { select { case update := <-subscription.Updates(): require.NotNil(t, update) + payment, ok := update.(*paymentsdb.MPPayment) + if !ok { + t.Fatalf("unexpected payment type: %T", update) + } + require.Equal( t, info.PaymentIdentifier, - update.(*channeldb.MPPayment).Info.PaymentIdentifier, + payment.Info.PaymentIdentifier, ) require.Len(t, subscription.Updates(), 0) + case <-time.After(testTimeout): require.Fail(t, "timeout waiting for payment result") } @@ -354,7 +374,7 @@ func TestKVPaymentsDBUnsubscribeSuccess(t *testing.T) { db := initDB(t) - paymentDB, err := channeldb.NewKVPaymentsDB( + paymentDB, err := paymentsdb.NewKVPaymentsDB( db, paymentsdb.WithKeepFailedPaymentAttempts(true), ) @@ -411,8 +431,8 @@ func TestKVPaymentsDBUnsubscribeSuccess(t *testing.T) { subscription2.Close() // Register another update. - failInfo := channeldb.HTLCFailInfo{ - Reason: channeldb.HTLCFailInternal, + failInfo := paymentsdb.HTLCFailInfo{ + Reason: paymentsdb.HTLCFailInternal, } _, err = pControl.FailAttempt( info.PaymentIdentifier, attempt.AttemptID, &failInfo, @@ -429,7 +449,7 @@ func testKVPaymentsDBSubscribeFail(t *testing.T, registerAttempt, db := initDB(t) - paymentDB, err := channeldb.NewKVPaymentsDB( + paymentDB, err := paymentsdb.NewKVPaymentsDB( db, paymentsdb.WithKeepFailedPaymentAttempts( keepFailedPaymentAttempts, @@ -465,8 +485,8 @@ func testKVPaymentsDBSubscribeFail(t *testing.T, registerAttempt, } // Fail the payment attempt. - failInfo := channeldb.HTLCFailInfo{ - Reason: channeldb.HTLCFailInternal, + failInfo := paymentsdb.HTLCFailInfo{ + Reason: paymentsdb.HTLCFailInternal, } htlcAttempt, err := pControl.FailAttempt( info.PaymentIdentifier, attempt.AttemptID, &failInfo, @@ -481,7 +501,7 @@ func testKVPaymentsDBSubscribeFail(t *testing.T, registerAttempt, // Mark the payment as failed. err = pControl.FailPayment( - info.PaymentIdentifier, channeldb.FailureReasonTimeout, + info.PaymentIdentifier, paymentsdb.FailureReasonTimeout, ) if err != nil { t.Fatal(err) @@ -498,17 +518,22 @@ func testKVPaymentsDBSubscribeFail(t *testing.T, registerAttempt, } for i, s := range subscribers { - var result *channeldb.MPPayment + var result *paymentsdb.MPPayment for result == nil || !result.Terminated() { select { case item := <-s.Updates(): - result = item.(*channeldb.MPPayment) + payment, ok := item.(*paymentsdb.MPPayment) + require.True( + t, ok, "unexpected payment type: %T", + item) + + result = payment case <-time.After(testTimeout): t.Fatal("timeout waiting for payment result") } } - if result.GetStatus() == channeldb.StatusSucceeded { + if result.GetStatus() == paymentsdb.StatusSucceeded { t.Fatal("unexpected payment state") } @@ -533,11 +558,11 @@ func testKVPaymentsDBSubscribeFail(t *testing.T, registerAttempt, len(result.HTLCs)) } - require.Equalf(t, channeldb.StatusFailed, result.GetStatus(), + require.Equalf(t, paymentsdb.StatusFailed, result.GetStatus(), "subscriber %v failed, want %s, got %s", i, - channeldb.StatusFailed, result.GetStatus()) + paymentsdb.StatusFailed, result.GetStatus()) - if *result.FailureReason != channeldb.FailureReasonTimeout { + if *result.FailureReason != paymentsdb.FailureReasonTimeout { t.Fatal("unexpected failure reason") } @@ -559,7 +584,7 @@ func initDB(t *testing.T) *channeldb.DB { ) } -func genInfo() (*channeldb.PaymentCreationInfo, *channeldb.HTLCAttemptInfo, +func genInfo() (*paymentsdb.PaymentCreationInfo, *paymentsdb.HTLCAttemptInfo, lntypes.Preimage, error) { preimage, err := genPreimage() @@ -572,14 +597,14 @@ func genInfo() (*channeldb.PaymentCreationInfo, *channeldb.HTLCAttemptInfo, var hash lntypes.Hash copy(hash[:], rhash[:]) - attempt, err := channeldb.NewHtlcAttempt( + attempt, err := paymentsdb.NewHtlcAttempt( 1, priv, testRoute, time.Time{}, &hash, ) if err != nil { return nil, nil, lntypes.Preimage{}, err } - return &channeldb.PaymentCreationInfo{ + return &paymentsdb.PaymentCreationInfo{ PaymentIdentifier: rhash, Value: testRoute.ReceiverAmt(), CreationTime: time.Unix(time.Now().Unix(), 0), diff --git a/routing/missioncontrol.go b/routing/missioncontrol.go index 89e14f523bc..b03724a2e62 100644 --- a/routing/missioncontrol.go +++ b/routing/missioncontrol.go @@ -11,11 +11,11 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btclog/v2" "github.com/btcsuite/btcwallet/walletdb" - "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lnwire" + paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/tlv" ) @@ -617,7 +617,7 @@ func (m *MissionControl) GetPairHistorySnapshot( // payment attempts need to be made. func (m *MissionControl) ReportPaymentFail(paymentID uint64, rt *route.Route, failureSourceIdx *int, failure lnwire.FailureMessage) ( - *channeldb.FailureReason, error) { + *paymentsdb.FailureReason, error) { timestamp := m.cfg.clock.Now() @@ -649,7 +649,7 @@ func (m *MissionControl) ReportPaymentSuccess(paymentID uint64, // processPaymentResult stores a payment result in the mission control store and // updates mission control's in-memory state. func (m *MissionControl) processPaymentResult(result *paymentResult) ( - *channeldb.FailureReason, error) { + *paymentsdb.FailureReason, error) { // Store complete result in database. m.store.AddResult(result) @@ -667,7 +667,7 @@ func (m *MissionControl) processPaymentResult(result *paymentResult) ( // estimates. It returns a bool indicating whether this error is a final error // and no further payment attempts need to be made. func (m *MissionControl) applyPaymentResult( - result *paymentResult) *channeldb.FailureReason { + result *paymentResult) *paymentsdb.FailureReason { // Interpret result. i := interpretResult(&result.route.Val, result.failure.ValOpt()) diff --git a/routing/mock_test.go b/routing/mock_test.go index d9528254e81..9cb938f74f2 100644 --- a/routing/mock_test.go +++ b/routing/mock_test.go @@ -7,7 +7,6 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" - "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/htlcswitch" @@ -134,12 +133,12 @@ var _ MissionControlQuerier = (*mockMissionControlOld)(nil) func (m *mockMissionControlOld) ReportPaymentFail( paymentID uint64, rt *route.Route, failureSourceIdx *int, failure lnwire.FailureMessage) ( - *channeldb.FailureReason, error) { + *paymentsdb.FailureReason, error) { // Report a permanent failure if this is an error caused // by incorrect details. if failure.Code() == lnwire.CodeIncorrectOrUnknownPaymentDetails { - reason := channeldb.FailureReasonPaymentDetails + reason := paymentsdb.FailureReasonPaymentDetails return &reason, nil } @@ -248,11 +247,11 @@ func (m *mockPayerOld) CleanStore(pids map[uint64]struct{}) error { } type initArgs struct { - c *channeldb.PaymentCreationInfo + c *paymentsdb.PaymentCreationInfo } type registerAttemptArgs struct { - a *channeldb.HTLCAttemptInfo + a *paymentsdb.HTLCAttemptInfo } type settleAttemptArgs struct { @@ -260,22 +259,22 @@ type settleAttemptArgs struct { } type failAttemptArgs struct { - reason *channeldb.HTLCFailInfo + reason *paymentsdb.HTLCFailInfo } type failPaymentArgs struct { - reason channeldb.FailureReason + reason paymentsdb.FailureReason } type testPayment struct { - info channeldb.PaymentCreationInfo - attempts []channeldb.HTLCAttempt + info paymentsdb.PaymentCreationInfo + attempts []paymentsdb.HTLCAttempt } type mockControlTowerOld struct { payments map[lntypes.Hash]*testPayment successful map[lntypes.Hash]struct{} - failed map[lntypes.Hash]channeldb.FailureReason + failed map[lntypes.Hash]paymentsdb.FailureReason init chan initArgs registerAttempt chan registerAttemptArgs @@ -293,12 +292,12 @@ func makeMockControlTower() *mockControlTowerOld { return &mockControlTowerOld{ payments: make(map[lntypes.Hash]*testPayment), successful: make(map[lntypes.Hash]struct{}), - failed: make(map[lntypes.Hash]channeldb.FailureReason), + failed: make(map[lntypes.Hash]paymentsdb.FailureReason), } } func (m *mockControlTowerOld) InitPayment(phash lntypes.Hash, - c *channeldb.PaymentCreationInfo) error { + c *paymentsdb.PaymentCreationInfo) error { if m.init != nil { m.init <- initArgs{c} @@ -355,7 +354,7 @@ func (m *mockControlTowerOld) DeleteFailedAttempts(phash lntypes.Hash) error { } func (m *mockControlTowerOld) RegisterAttempt(phash lntypes.Hash, - a *channeldb.HTLCAttemptInfo) error { + a *paymentsdb.HTLCAttemptInfo) error { if m.registerAttempt != nil { m.registerAttempt <- registerAttemptArgs{a} @@ -400,7 +399,7 @@ func (m *mockControlTowerOld) RegisterAttempt(phash lntypes.Hash, } // Add attempt to payment. - p.attempts = append(p.attempts, channeldb.HTLCAttempt{ + p.attempts = append(p.attempts, paymentsdb.HTLCAttempt{ HTLCAttemptInfo: *a, }) m.payments[phash] = p @@ -409,8 +408,8 @@ func (m *mockControlTowerOld) RegisterAttempt(phash lntypes.Hash, } func (m *mockControlTowerOld) SettleAttempt(phash lntypes.Hash, - pid uint64, settleInfo *channeldb.HTLCSettleInfo) ( - *channeldb.HTLCAttempt, error) { + pid uint64, settleInfo *paymentsdb.HTLCSettleInfo) ( + *paymentsdb.HTLCAttempt, error) { if m.settleAttempt != nil { m.settleAttempt <- settleAttemptArgs{settleInfo.Preimage} @@ -442,7 +441,8 @@ func (m *mockControlTowerOld) SettleAttempt(phash lntypes.Hash, // Mark the payment successful on first settled attempt. m.successful[phash] = struct{}{} - return &channeldb.HTLCAttempt{ + + return &paymentsdb.HTLCAttempt{ Settle: settleInfo, }, nil } @@ -451,7 +451,7 @@ func (m *mockControlTowerOld) SettleAttempt(phash lntypes.Hash, } func (m *mockControlTowerOld) FailAttempt(phash lntypes.Hash, pid uint64, - failInfo *channeldb.HTLCFailInfo) (*channeldb.HTLCAttempt, error) { + failInfo *paymentsdb.HTLCFailInfo) (*paymentsdb.HTLCAttempt, error) { if m.failAttempt != nil { m.failAttempt <- failAttemptArgs{failInfo} @@ -480,7 +480,8 @@ func (m *mockControlTowerOld) FailAttempt(phash lntypes.Hash, pid uint64, } p.attempts[i].Failure = failInfo - return &channeldb.HTLCAttempt{ + + return &paymentsdb.HTLCAttempt{ Failure: failInfo, }, nil } @@ -489,7 +490,7 @@ func (m *mockControlTowerOld) FailAttempt(phash lntypes.Hash, pid uint64, } func (m *mockControlTowerOld) FailPayment(phash lntypes.Hash, - reason channeldb.FailureReason) error { + reason paymentsdb.FailureReason) error { m.Lock() defer m.Unlock() @@ -518,14 +519,14 @@ func (m *mockControlTowerOld) FetchPayment(phash lntypes.Hash) ( } func (m *mockControlTowerOld) fetchPayment(phash lntypes.Hash) ( - *channeldb.MPPayment, error) { + *paymentsdb.MPPayment, error) { p, ok := m.payments[phash] if !ok { return nil, paymentsdb.ErrPaymentNotInitiated } - mp := &channeldb.MPPayment{ + mp := &paymentsdb.MPPayment{ Info: &p.info, } @@ -545,7 +546,7 @@ func (m *mockControlTowerOld) fetchPayment(phash lntypes.Hash) ( } func (m *mockControlTowerOld) FetchInFlightPayments() ( - []*channeldb.MPPayment, error) { + []*paymentsdb.MPPayment, error) { if m.fetchInFlight != nil { m.fetchInFlight <- struct{}{} @@ -555,7 +556,7 @@ func (m *mockControlTowerOld) FetchInFlightPayments() ( defer m.Unlock() // In flight are all payments not successful or failed. - var fl []*channeldb.MPPayment + var fl []*paymentsdb.MPPayment for hash := range m.payments { if _, ok := m.successful[hash]; ok { continue @@ -664,7 +665,7 @@ var _ MissionControlQuerier = (*mockMissionControl)(nil) func (m *mockMissionControl) ReportPaymentFail( paymentID uint64, rt *route.Route, failureSourceIdx *int, failure lnwire.FailureMessage) ( - *channeldb.FailureReason, error) { + *paymentsdb.FailureReason, error) { args := m.Called(paymentID, rt, failureSourceIdx, failure) @@ -673,7 +674,7 @@ func (m *mockMissionControl) ReportPaymentFail( return nil, args.Error(1) } - return args.Get(0).(*channeldb.FailureReason), args.Error(1) + return args.Get(0).(*paymentsdb.FailureReason), args.Error(1) } func (m *mockMissionControl) ReportPaymentSuccess(paymentID uint64, @@ -733,7 +734,7 @@ type mockControlTower struct { var _ ControlTower = (*mockControlTower)(nil) func (m *mockControlTower) InitPayment(phash lntypes.Hash, - c *channeldb.PaymentCreationInfo) error { + c *paymentsdb.PaymentCreationInfo) error { args := m.Called(phash, c) return args.Error(0) @@ -745,15 +746,15 @@ func (m *mockControlTower) DeleteFailedAttempts(phash lntypes.Hash) error { } func (m *mockControlTower) RegisterAttempt(phash lntypes.Hash, - a *channeldb.HTLCAttemptInfo) error { + a *paymentsdb.HTLCAttemptInfo) error { args := m.Called(phash, a) return args.Error(0) } func (m *mockControlTower) SettleAttempt(phash lntypes.Hash, - pid uint64, settleInfo *channeldb.HTLCSettleInfo) ( - *channeldb.HTLCAttempt, error) { + pid uint64, settleInfo *paymentsdb.HTLCSettleInfo) ( + *paymentsdb.HTLCAttempt, error) { args := m.Called(phash, pid, settleInfo) @@ -762,11 +763,11 @@ func (m *mockControlTower) SettleAttempt(phash lntypes.Hash, return nil, args.Error(1) } - return attempt.(*channeldb.HTLCAttempt), args.Error(1) + return attempt.(*paymentsdb.HTLCAttempt), args.Error(1) } func (m *mockControlTower) FailAttempt(phash lntypes.Hash, pid uint64, - failInfo *channeldb.HTLCFailInfo) (*channeldb.HTLCAttempt, error) { + failInfo *paymentsdb.HTLCFailInfo) (*paymentsdb.HTLCAttempt, error) { args := m.Called(phash, pid, failInfo) @@ -775,11 +776,11 @@ func (m *mockControlTower) FailAttempt(phash lntypes.Hash, pid uint64, return nil, args.Error(1) } - return args.Get(0).(*channeldb.HTLCAttempt), args.Error(1) + return attempt.(*paymentsdb.HTLCAttempt), args.Error(1) } func (m *mockControlTower) FailPayment(phash lntypes.Hash, - reason channeldb.FailureReason) error { + reason paymentsdb.FailureReason) error { args := m.Called(phash, reason) return args.Error(0) @@ -800,10 +801,10 @@ func (m *mockControlTower) FetchPayment(phash lntypes.Hash) ( } func (m *mockControlTower) FetchInFlightPayments() ( - []*channeldb.MPPayment, error) { + []*paymentsdb.MPPayment, error) { args := m.Called() - return args.Get(0).([]*channeldb.MPPayment), args.Error(1) + return args.Get(0).([]*paymentsdb.MPPayment), args.Error(1) } func (m *mockControlTower) SubscribePayment(paymentHash lntypes.Hash) ( @@ -826,14 +827,14 @@ type mockMPPayment struct { var _ DBMPPayment = (*mockMPPayment)(nil) -func (m *mockMPPayment) GetState() *channeldb.MPPaymentState { +func (m *mockMPPayment) GetState() *paymentsdb.MPPaymentState { args := m.Called() - return args.Get(0).(*channeldb.MPPaymentState) + return args.Get(0).(*paymentsdb.MPPaymentState) } -func (m *mockMPPayment) GetStatus() channeldb.PaymentStatus { +func (m *mockMPPayment) GetStatus() paymentsdb.PaymentStatus { args := m.Called() - return args.Get(0).(channeldb.PaymentStatus) + return args.Get(0).(paymentsdb.PaymentStatus) } func (m *mockMPPayment) Terminated() bool { @@ -847,14 +848,14 @@ func (m *mockMPPayment) NeedWaitAttempts() (bool, error) { return args.Bool(0), args.Error(1) } -func (m *mockMPPayment) GetHTLCs() []channeldb.HTLCAttempt { +func (m *mockMPPayment) GetHTLCs() []paymentsdb.HTLCAttempt { args := m.Called() - return args.Get(0).([]channeldb.HTLCAttempt) + return args.Get(0).([]paymentsdb.HTLCAttempt) } -func (m *mockMPPayment) InFlightHTLCs() []channeldb.HTLCAttempt { +func (m *mockMPPayment) InFlightHTLCs() []paymentsdb.HTLCAttempt { args := m.Called() - return args.Get(0).([]channeldb.HTLCAttempt) + return args.Get(0).([]paymentsdb.HTLCAttempt) } func (m *mockMPPayment) AllowMoreAttempts() (bool, error) { @@ -862,24 +863,24 @@ func (m *mockMPPayment) AllowMoreAttempts() (bool, error) { return args.Bool(0), args.Error(1) } -func (m *mockMPPayment) TerminalInfo() (*channeldb.HTLCAttempt, - *channeldb.FailureReason) { +func (m *mockMPPayment) TerminalInfo() (*paymentsdb.HTLCAttempt, + *paymentsdb.FailureReason) { args := m.Called() var ( - settleInfo *channeldb.HTLCAttempt - failureInfo *channeldb.FailureReason + settleInfo *paymentsdb.HTLCAttempt + failureInfo *paymentsdb.FailureReason ) settle := args.Get(0) if settle != nil { - settleInfo = settle.(*channeldb.HTLCAttempt) + settleInfo = settle.(*paymentsdb.HTLCAttempt) } reason := args.Get(1) if reason != nil { - failureInfo = reason.(*channeldb.FailureReason) + failureInfo = reason.(*paymentsdb.FailureReason) } return settleInfo, failureInfo diff --git a/routing/payment_lifecycle.go b/routing/payment_lifecycle.go index a42b21de58b..245e3b3c151 100644 --- a/routing/payment_lifecycle.go +++ b/routing/payment_lifecycle.go @@ -9,13 +9,13 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/davecgh/go-spew/spew" sphinx "github.com/lightningnetwork/lightning-onion" - "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnwire" + paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/routing/shards" "github.com/lightningnetwork/lnd/tlv" @@ -29,7 +29,7 @@ var ErrPaymentLifecycleExiting = errors.New("payment lifecycle exiting") // HTLC. type switchResult struct { // attempt is the HTLC sent to the switch. - attempt *channeldb.HTLCAttempt + attempt *paymentsdb.HTLCAttempt // result is sent from the switch which contains either a preimage if // ths HTLC is settled or an error if it's failed. @@ -59,7 +59,7 @@ type paymentLifecycle struct { // an HTLC attempt, which is always mounted to `p.collectResultAsync` // except in unit test, where we use a much simpler resultCollector to // decouple the test flow for the payment lifecycle. - resultCollector func(attempt *channeldb.HTLCAttempt) + resultCollector func(attempt *paymentsdb.HTLCAttempt) } // newPaymentLifecycle initiates a new payment lifecycle and returns it. @@ -352,13 +352,13 @@ func (p *paymentLifecycle) checkContext(ctx context.Context) error { // user-provided timeout was reached, or the context was // canceled, either to a manual cancellation or due to an // unknown error. - var reason channeldb.FailureReason + var reason paymentsdb.FailureReason if errors.Is(ctx.Err(), context.DeadlineExceeded) { - reason = channeldb.FailureReasonTimeout + reason = paymentsdb.FailureReasonTimeout log.Warnf("Payment attempt not completed before "+ "context timeout, id=%s", p.identifier.String()) } else { - reason = channeldb.FailureReasonCanceled + reason = paymentsdb.FailureReasonCanceled log.Warnf("Payment attempt context canceled, id=%s", p.identifier.String()) } @@ -386,7 +386,7 @@ func (p *paymentLifecycle) checkContext(ctx context.Context) error { // requestRoute is responsible for finding a route to be used to create an HTLC // attempt. func (p *paymentLifecycle) requestRoute( - ps *channeldb.MPPaymentState) (*route.Route, error) { + ps *paymentsdb.MPPaymentState) (*route.Route, error) { remainingFees := p.calcFeeBudget(ps.FeesPaid) @@ -455,14 +455,14 @@ type attemptResult struct { err error // attempt is the attempt structure as recorded in the database. - attempt *channeldb.HTLCAttempt + attempt *paymentsdb.HTLCAttempt } // collectResultAsync launches a goroutine that will wait for the result of the // given HTLC attempt to be available then save its result in a map. Once // received, it will send the result returned from the switch to channel // `resultCollected`. -func (p *paymentLifecycle) collectResultAsync(attempt *channeldb.HTLCAttempt) { +func (p *paymentLifecycle) collectResultAsync(attempt *paymentsdb.HTLCAttempt) { log.Debugf("Collecting result for attempt %v in payment %v", attempt.AttemptID, p.identifier) @@ -504,7 +504,7 @@ func (p *paymentLifecycle) collectResultAsync(attempt *channeldb.HTLCAttempt) { // collectResult waits for the result of the given HTLC attempt to be sent by // the switch and returns it. func (p *paymentLifecycle) collectResult( - attempt *channeldb.HTLCAttempt) (*htlcswitch.PaymentResult, error) { + attempt *paymentsdb.HTLCAttempt) (*htlcswitch.PaymentResult, error) { log.Tracef("Collecting result for attempt %v", lnutils.SpewLogClosure(attempt)) @@ -581,7 +581,7 @@ func (p *paymentLifecycle) collectResult( // by using the route info provided. The `remainingAmt` is used to decide // whether this is the last attempt. func (p *paymentLifecycle) registerAttempt(rt *route.Route, - remainingAmt lnwire.MilliSatoshi) (*channeldb.HTLCAttempt, error) { + remainingAmt lnwire.MilliSatoshi) (*paymentsdb.HTLCAttempt, error) { // If this route will consume the last remaining amount to send // to the receiver, this will be our last shard (for now). @@ -608,7 +608,7 @@ func (p *paymentLifecycle) registerAttempt(rt *route.Route, // createNewPaymentAttempt creates a new payment attempt from the given route. func (p *paymentLifecycle) createNewPaymentAttempt(rt *route.Route, - lastShard bool) (*channeldb.HTLCAttempt, error) { + lastShard bool) (*paymentsdb.HTLCAttempt, error) { // Generate a new key to be used for this attempt. sessionKey, err := generateNewSessionKey() @@ -648,7 +648,7 @@ func (p *paymentLifecycle) createNewPaymentAttempt(rt *route.Route, // We now have all the information needed to populate the current // attempt information. - return channeldb.NewHtlcAttempt( + return paymentsdb.NewHtlcAttempt( attemptID, sessionKey, *rt, p.router.cfg.Clock.Now(), &hash, ) } @@ -657,7 +657,7 @@ func (p *paymentLifecycle) createNewPaymentAttempt(rt *route.Route, // the payment. If this attempt fails, then we'll continue on to the next // available route. func (p *paymentLifecycle) sendAttempt( - attempt *channeldb.HTLCAttempt) (*attemptResult, error) { + attempt *paymentsdb.HTLCAttempt) (*attemptResult, error) { log.Debugf("Sending HTLC attempt(id=%v, total_amt=%v, first_hop_amt=%d"+ ") for payment %v", attempt.AttemptID, @@ -794,7 +794,7 @@ func (p *paymentLifecycle) amendFirstHopData(rt *route.Route) error { // failAttemptAndPayment fails both the payment and its attempt via the // router's control tower, which marks the payment as failed in db. func (p *paymentLifecycle) failPaymentAndAttempt( - attemptID uint64, reason *channeldb.FailureReason, + attemptID uint64, reason *paymentsdb.FailureReason, sendErr error) (*attemptResult, error) { log.Errorf("Payment %v failed: final_outcome=%v, raw_err=%v", @@ -823,10 +823,10 @@ func (p *paymentLifecycle) failPaymentAndAttempt( // the error type, the error is either the final outcome of the payment or we // need to continue with an alternative route. A final outcome is indicated by // a non-nil reason value. -func (p *paymentLifecycle) handleSwitchErr(attempt *channeldb.HTLCAttempt, +func (p *paymentLifecycle) handleSwitchErr(attempt *paymentsdb.HTLCAttempt, sendErr error) (*attemptResult, error) { - internalErrorReason := channeldb.FailureReasonError + internalErrorReason := paymentsdb.FailureReasonError attemptID := attempt.AttemptID // reportAndFail is a helper closure that reports the failure to the @@ -1030,34 +1030,34 @@ func (p *paymentLifecycle) failAttempt(attemptID uint64, // marshallError marshall an error as received from the switch to a structure // that is suitable for database storage. -func marshallError(sendError error, time time.Time) *channeldb.HTLCFailInfo { - response := &channeldb.HTLCFailInfo{ +func marshallError(sendError error, time time.Time) *paymentsdb.HTLCFailInfo { + response := &paymentsdb.HTLCFailInfo{ FailTime: time, } switch { case errors.Is(sendError, htlcswitch.ErrPaymentIDNotFound): - response.Reason = channeldb.HTLCFailInternal + response.Reason = paymentsdb.HTLCFailInternal return response case errors.Is(sendError, htlcswitch.ErrUnreadableFailureMessage): - response.Reason = channeldb.HTLCFailUnreadable + response.Reason = paymentsdb.HTLCFailUnreadable return response } var rtErr htlcswitch.ClearTextError ok := errors.As(sendError, &rtErr) if !ok { - response.Reason = channeldb.HTLCFailInternal + response.Reason = paymentsdb.HTLCFailInternal return response } message := rtErr.WireMessage() if message != nil { - response.Reason = channeldb.HTLCFailMessage + response.Reason = paymentsdb.HTLCFailMessage response.Message = message } else { - response.Reason = channeldb.HTLCFailUnknown + response.Reason = paymentsdb.HTLCFailUnknown } // If the ClearTextError received is a ForwardingError, the error @@ -1082,7 +1082,7 @@ func marshallError(sendError error, time time.Time) *channeldb.HTLCFailInfo { // enabled, the `Hash` field in their HTLC attempts is nil. In that case, we use // the payment hash as the `attempt.Hash` as they are identical. func (p *paymentLifecycle) patchLegacyPaymentHash( - a channeldb.HTLCAttempt) channeldb.HTLCAttempt { + a paymentsdb.HTLCAttempt) paymentsdb.HTLCAttempt { // Exit early if this is not a legacy attempt. if a.Hash != nil { @@ -1134,7 +1134,7 @@ func (p *paymentLifecycle) reloadInflightAttempts() (DBMPPayment, error) { // reloadPayment returns the latest payment found in the db (control tower). func (p *paymentLifecycle) reloadPayment() (DBMPPayment, - *channeldb.MPPaymentState, error) { + *paymentsdb.MPPaymentState, error) { // Read the db to get the latest state of the payment. payment, err := p.router.cfg.Control.FetchPayment(p.identifier) @@ -1154,7 +1154,7 @@ func (p *paymentLifecycle) reloadPayment() (DBMPPayment, // handleAttemptResult processes the result of an HTLC attempt returned from // the htlcswitch. -func (p *paymentLifecycle) handleAttemptResult(attempt *channeldb.HTLCAttempt, +func (p *paymentLifecycle) handleAttemptResult(attempt *paymentsdb.HTLCAttempt, result *htlcswitch.PaymentResult) (*attemptResult, error) { // If the result has an error, we need to further process it by failing @@ -1179,7 +1179,7 @@ func (p *paymentLifecycle) handleAttemptResult(attempt *channeldb.HTLCAttempt, // move the shard to the settled state. htlcAttempt, err := p.router.cfg.Control.SettleAttempt( p.identifier, attempt.AttemptID, - &channeldb.HTLCSettleInfo{ + &paymentsdb.HTLCSettleInfo{ Preimage: result.Preimage, SettleTime: p.router.cfg.Clock.Now(), }, @@ -1204,7 +1204,7 @@ func (p *paymentLifecycle) handleAttemptResult(attempt *channeldb.HTLCAttempt, // tower. An attemptResult is returned, indicating the final outcome of this // HTLC attempt. func (p *paymentLifecycle) collectAndHandleResult( - attempt *channeldb.HTLCAttempt) (*attemptResult, error) { + attempt *paymentsdb.HTLCAttempt) (*attemptResult, error) { result, err := p.collectResult(attempt) if err != nil { diff --git a/routing/payment_lifecycle_test.go b/routing/payment_lifecycle_test.go index a4f455039d7..8094f507574 100644 --- a/routing/payment_lifecycle_test.go +++ b/routing/payment_lifecycle_test.go @@ -8,13 +8,13 @@ import ( "time" "github.com/btcsuite/btcd/btcec/v2" - "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/lnmock" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" + paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/routing/route" "github.com/stretchr/testify/mock" @@ -116,7 +116,7 @@ func newTestPaymentLifecycle(t *testing.T) (*paymentLifecycle, *mockers) { // Overwrite the collectResultAsync to focus on testing the payment // lifecycle within the goroutine. - resultCollector := func(attempt *channeldb.HTLCAttempt) { + resultCollector := func(attempt *paymentsdb.HTLCAttempt) { mockers.collectResultsCount++ } p.resultCollector = resultCollector @@ -147,7 +147,7 @@ func setupTestPaymentLifecycle(t *testing.T) (*paymentLifecycle, *mockers) { m.payment, nil, ).Once() - htlcs := []channeldb.HTLCAttempt{} + htlcs := []paymentsdb.HTLCAttempt{} m.payment.On("InFlightHTLCs").Return(htlcs).Once() return p, m @@ -258,11 +258,11 @@ func createDummyRoute(t *testing.T, amt lnwire.MilliSatoshi) *route.Route { } func makeSettledAttempt(t *testing.T, total int, - preimage lntypes.Preimage) *channeldb.HTLCAttempt { + preimage lntypes.Preimage) *paymentsdb.HTLCAttempt { - a := &channeldb.HTLCAttempt{ + a := &paymentsdb.HTLCAttempt{ HTLCAttemptInfo: makeAttemptInfo(t, total), - Settle: &channeldb.HTLCSettleInfo{Preimage: preimage}, + Settle: &paymentsdb.HTLCSettleInfo{Preimage: preimage}, } hash := preimage.Hash() @@ -271,18 +271,18 @@ func makeSettledAttempt(t *testing.T, total int, return a } -func makeFailedAttempt(t *testing.T, total int) *channeldb.HTLCAttempt { - return &channeldb.HTLCAttempt{ +func makeFailedAttempt(t *testing.T, total int) *paymentsdb.HTLCAttempt { + return &paymentsdb.HTLCAttempt{ HTLCAttemptInfo: makeAttemptInfo(t, total), - Failure: &channeldb.HTLCFailInfo{ - Reason: channeldb.HTLCFailInternal, + Failure: &paymentsdb.HTLCFailInfo{ + Reason: paymentsdb.HTLCFailInternal, }, } } -func makeAttemptInfo(t *testing.T, amt int) channeldb.HTLCAttemptInfo { +func makeAttemptInfo(t *testing.T, amt int) paymentsdb.HTLCAttemptInfo { rt := createDummyRoute(t, lnwire.MilliSatoshi(amt)) - return channeldb.HTLCAttemptInfo{ + return paymentsdb.HTLCAttemptInfo{ Route: *rt, Hash: &lntypes.Hash{1, 2, 3}, } @@ -302,7 +302,7 @@ func TestCheckTimeoutTimedOut(t *testing.T) { // Mock the control tower's `FailPayment` method. ct := &mockControlTower{} ct.On("FailPayment", - p.identifier, channeldb.FailureReasonTimeout).Return(nil) + p.identifier, paymentsdb.FailureReasonTimeout).Return(nil) // Mount the mocked control tower. p.router.cfg.Control = ct @@ -323,7 +323,7 @@ func TestCheckTimeoutTimedOut(t *testing.T) { // Mock `FailPayment` to return a dummy error. ct = &mockControlTower{} ct.On("FailPayment", - p.identifier, channeldb.FailureReasonTimeout).Return(errDummy) + p.identifier, paymentsdb.FailureReasonTimeout).Return(errDummy) // Mount the mocked control tower. p.router.cfg.Control = ct @@ -378,7 +378,7 @@ func TestRequestRouteSucceed(t *testing.T) { p.paySession = paySession // Create a dummy payment state. - ps := &channeldb.MPPaymentState{ + ps := &paymentsdb.MPPaymentState{ NumAttemptsInFlight: 1, RemainingAmt: 1, FeesPaid: 100, @@ -415,7 +415,7 @@ func TestRequestRouteHandleCriticalErr(t *testing.T) { p.paySession = paySession // Create a dummy payment state. - ps := &channeldb.MPPaymentState{ + ps := &paymentsdb.MPPaymentState{ NumAttemptsInFlight: 1, RemainingAmt: 1, FeesPaid: 100, @@ -449,7 +449,7 @@ func TestRequestRouteHandleNoRouteErr(t *testing.T) { p, m := newTestPaymentLifecycle(t) // Create a dummy payment state. - ps := &channeldb.MPPaymentState{ + ps := &paymentsdb.MPPaymentState{ NumAttemptsInFlight: 1, RemainingAmt: 1, FeesPaid: 100, @@ -467,7 +467,7 @@ func TestRequestRouteHandleNoRouteErr(t *testing.T) { // The payment should be failed with reason no route. m.control.On("FailPayment", - p.identifier, channeldb.FailureReasonNoRoute, + p.identifier, paymentsdb.FailureReasonNoRoute, ).Return(nil).Once() result, err := p.requestRoute(ps) @@ -498,7 +498,7 @@ func TestRequestRouteFailPaymentError(t *testing.T) { p.paySession = paySession // Create a dummy payment state with zero inflight attempts. - ps := &channeldb.MPPaymentState{ + ps := &paymentsdb.MPPaymentState{ NumAttemptsInFlight: 0, RemainingAmt: 1, FeesPaid: 100, @@ -819,14 +819,14 @@ func TestResumePaymentFailOnTimeout(t *testing.T) { m.control.On("FetchPayment", p.identifier).Return(m.payment, nil).Once() // 2. calls `GetState` and return the state. - ps := &channeldb.MPPaymentState{ + ps := &paymentsdb.MPPaymentState{ RemainingAmt: paymentAmt, } m.payment.On("GetState").Return(ps).Once() // NOTE: GetStatus is only used to populate the logs which is not // critical, so we loosen the checks on how many times it's been called. - m.payment.On("GetStatus").Return(channeldb.StatusInFlight) + m.payment.On("GetStatus").Return(paymentsdb.StatusInFlight) // 3. make the timeout happens instantly and sleep one millisecond to // make sure it timed out. @@ -837,7 +837,7 @@ func TestResumePaymentFailOnTimeout(t *testing.T) { // 4. the payment should be failed with reason timeout. m.control.On("FailPayment", - p.identifier, channeldb.FailureReasonTimeout, + p.identifier, paymentsdb.FailureReasonTimeout, ).Return(nil).Once() // 5. decideNextStep now returns stepExit. @@ -848,7 +848,7 @@ func TestResumePaymentFailOnTimeout(t *testing.T) { m.control.On("DeleteFailedAttempts", p.identifier).Return(nil).Once() // 7. the payment returns the failed reason. - reason := channeldb.FailureReasonTimeout + reason := paymentsdb.FailureReasonTimeout m.payment.On("TerminalInfo").Return(nil, &reason) // Send the payment and assert it failed with the timeout reason. @@ -875,7 +875,7 @@ func TestResumePaymentFailOnTimeoutErr(t *testing.T) { // NOTE: GetStatus is only used to populate the logs which is // not critical so we loosen the checks on how many times it's // been called. - m.payment.On("GetStatus").Return(channeldb.StatusInFlight) + m.payment.On("GetStatus").Return(paymentsdb.StatusInFlight) // Quit the router to return an error. close(p.router.quit) @@ -910,21 +910,21 @@ func TestResumePaymentFailContextCancel(t *testing.T) { m.control.On("FetchPayment", p.identifier).Return(m.payment, nil).Once() // 2. calls `GetState` and return the state. - ps := &channeldb.MPPaymentState{ + ps := &paymentsdb.MPPaymentState{ RemainingAmt: paymentAmt, } m.payment.On("GetState").Return(ps).Once() // NOTE: GetStatus is only used to populate the logs which is not // critical, so we loosen the checks on how many times it's been called. - m.payment.On("GetStatus").Return(channeldb.StatusInFlight) + m.payment.On("GetStatus").Return(paymentsdb.StatusInFlight) // 3. Cancel the context and skip the FailPayment error to trigger the // context cancellation of the payment. cancel() m.control.On( - "FailPayment", p.identifier, channeldb.FailureReasonCanceled, + "FailPayment", p.identifier, paymentsdb.FailureReasonCanceled, ).Return(nil).Once() // 4. decideNextStep now returns stepExit. @@ -935,7 +935,7 @@ func TestResumePaymentFailContextCancel(t *testing.T) { m.control.On("DeleteFailedAttempts", p.identifier).Return(nil).Once() // 6. We will observe FailureReasonError if the context was cancelled. - reason := channeldb.FailureReasonError + reason := paymentsdb.FailureReasonError m.payment.On("TerminalInfo").Return(nil, &reason) // Send the payment and assert it failed with the timeout reason. @@ -963,7 +963,7 @@ func TestResumePaymentFailOnStepErr(t *testing.T) { m.control.On("FetchPayment", p.identifier).Return(m.payment, nil).Once() // 2. calls `GetState` and return the state. - ps := &channeldb.MPPaymentState{ + ps := &paymentsdb.MPPaymentState{ RemainingAmt: paymentAmt, } m.payment.On("GetState").Return(ps).Once() @@ -971,7 +971,7 @@ func TestResumePaymentFailOnStepErr(t *testing.T) { // NOTE: GetStatus is only used to populate the logs which is // not critical so we loosen the checks on how many times it's // been called. - m.payment.On("GetStatus").Return(channeldb.StatusInFlight) + m.payment.On("GetStatus").Return(paymentsdb.StatusInFlight) // 3. decideNextStep now returns an error. m.payment.On("AllowMoreAttempts").Return(false, errDummy).Once() @@ -1001,7 +1001,7 @@ func TestResumePaymentFailOnRequestRouteErr(t *testing.T) { m.control.On("FetchPayment", p.identifier).Return(m.payment, nil).Once() // 2. calls `GetState` and return the state. - ps := &channeldb.MPPaymentState{ + ps := &paymentsdb.MPPaymentState{ RemainingAmt: paymentAmt, } m.payment.On("GetState").Return(ps).Once() @@ -1009,7 +1009,7 @@ func TestResumePaymentFailOnRequestRouteErr(t *testing.T) { // NOTE: GetStatus is only used to populate the logs which is // not critical so we loosen the checks on how many times it's // been called. - m.payment.On("GetStatus").Return(channeldb.StatusInFlight) + m.payment.On("GetStatus").Return(paymentsdb.StatusInFlight) // 3. decideNextStep now returns stepProceed. m.payment.On("AllowMoreAttempts").Return(true, nil).Once() @@ -1047,7 +1047,7 @@ func TestResumePaymentFailOnRegisterAttemptErr(t *testing.T) { m.control.On("FetchPayment", p.identifier).Return(m.payment, nil).Once() // 2. calls `GetState` and return the state. - ps := &channeldb.MPPaymentState{ + ps := &paymentsdb.MPPaymentState{ RemainingAmt: paymentAmt, } m.payment.On("GetState").Return(ps).Once() @@ -1055,7 +1055,7 @@ func TestResumePaymentFailOnRegisterAttemptErr(t *testing.T) { // NOTE: GetStatus is only used to populate the logs which is // not critical so we loosen the checks on how many times it's // been called. - m.payment.On("GetStatus").Return(channeldb.StatusInFlight) + m.payment.On("GetStatus").Return(paymentsdb.StatusInFlight) // 3. decideNextStep now returns stepProceed. m.payment.On("AllowMoreAttempts").Return(true, nil).Once() @@ -1107,7 +1107,7 @@ func TestResumePaymentFailOnSendAttemptErr(t *testing.T) { m.control.On("FetchPayment", p.identifier).Return(m.payment, nil).Once() // 2. calls `GetState` and return the state. - ps := &channeldb.MPPaymentState{ + ps := &paymentsdb.MPPaymentState{ RemainingAmt: paymentAmt, } m.payment.On("GetState").Return(ps).Once() @@ -1115,7 +1115,7 @@ func TestResumePaymentFailOnSendAttemptErr(t *testing.T) { // NOTE: GetStatus is only used to populate the logs which is // not critical so we loosen the checks on how many times it's // been called. - m.payment.On("GetStatus").Return(channeldb.StatusInFlight) + m.payment.On("GetStatus").Return(paymentsdb.StatusInFlight) // 3. decideNextStep now returns stepProceed. m.payment.On("AllowMoreAttempts").Return(true, nil).Once() @@ -1161,7 +1161,7 @@ func TestResumePaymentFailOnSendAttemptErr(t *testing.T) { // which we'd fail the payment, cancel the shard and fail the attempt. // // `FailPayment` should be called with an internal reason. - reason := channeldb.FailureReasonError + reason := paymentsdb.FailureReasonError m.control.On("FailPayment", p.identifier, reason).Return(nil).Once() // `CancelShard` should be called with the attemptID. @@ -1199,7 +1199,7 @@ func TestResumePaymentSuccess(t *testing.T) { m.control.On("FetchPayment", p.identifier).Return(m.payment, nil).Once() // 1.2. calls `GetState` and return the state. - ps := &channeldb.MPPaymentState{ + ps := &paymentsdb.MPPaymentState{ RemainingAmt: paymentAmt, } m.payment.On("GetState").Return(ps).Once() @@ -1207,7 +1207,7 @@ func TestResumePaymentSuccess(t *testing.T) { // NOTE: GetStatus is only used to populate the logs which is // not critical so we loosen the checks on how many times it's // been called. - m.payment.On("GetStatus").Return(channeldb.StatusInFlight) + m.payment.On("GetStatus").Return(paymentsdb.StatusInFlight) // 1.3. decideNextStep now returns stepProceed. m.payment.On("AllowMoreAttempts").Return(true, nil).Once() @@ -1300,7 +1300,7 @@ func TestResumePaymentSuccessWithTwoAttempts(t *testing.T) { m.control.On("FetchPayment", p.identifier).Return(m.payment, nil).Once() // 1.2. calls `GetState` and return the state. - ps := &channeldb.MPPaymentState{ + ps := &paymentsdb.MPPaymentState{ RemainingAmt: paymentAmt, } m.payment.On("GetState").Return(ps).Once() @@ -1308,7 +1308,7 @@ func TestResumePaymentSuccessWithTwoAttempts(t *testing.T) { // NOTE: GetStatus is only used to populate the logs which is // not critical so we loosen the checks on how many times it's // been called. - m.payment.On("GetStatus").Return(channeldb.StatusInFlight) + m.payment.On("GetStatus").Return(paymentsdb.StatusInFlight) // 1.3. decideNextStep now returns stepProceed. m.payment.On("AllowMoreAttempts").Return(true, nil).Once() @@ -1452,7 +1452,7 @@ func TestCollectResultExitOnErr(t *testing.T) { // which we'd fail the payment, cancel the shard and fail the attempt. // // `FailPayment` should be called with an internal reason. - reason := channeldb.FailureReasonError + reason := paymentsdb.FailureReasonError m.control.On("FailPayment", p.identifier, reason).Return(nil).Once() // `CancelShard` should be called with the attemptID. @@ -1498,7 +1498,7 @@ func TestCollectResultExitOnResultErr(t *testing.T) { // which we'd fail the payment, cancel the shard and fail the attempt. // // `FailPayment` should be called with an internal reason. - reason := channeldb.FailureReasonError + reason := paymentsdb.FailureReasonError m.control.On("FailPayment", p.identifier, reason).Return(nil).Once() // `CancelShard` should be called with the attemptID. @@ -1833,7 +1833,7 @@ func TestReloadInflightAttemptsLegacy(t *testing.T) { m.control.On("FetchPayment", p.identifier).Return(m.payment, nil).Once() // 2. calls `InFlightHTLCs` and return the attempt. - attempts := []channeldb.HTLCAttempt{*attempt} + attempts := []paymentsdb.HTLCAttempt{*attempt} m.payment.On("InFlightHTLCs").Return(attempts).Once() // 3. Mock the htlcswitch to return a the result chan. diff --git a/routing/payment_session.go b/routing/payment_session.go index cf20251ed4b..bb795211c21 100644 --- a/routing/payment_session.go +++ b/routing/payment_session.go @@ -5,12 +5,12 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btclog/v2" - "github.com/lightningnetwork/lnd/channeldb" graphdb "github.com/lightningnetwork/lnd/graph/db" "github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/netann" + paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/lightningnetwork/lnd/routing/route" ) @@ -104,7 +104,7 @@ func (e noRouteError) Error() string { } // FailureReason converts a path finding error into a payment-level failure. -func (e noRouteError) FailureReason() channeldb.FailureReason { +func (e noRouteError) FailureReason() paymentsdb.FailureReason { switch e { case errNoTlvPayload, @@ -114,13 +114,13 @@ func (e noRouteError) FailureReason() channeldb.FailureReason { errUnknownRequiredFeature, errMissingDependentFeature: - return channeldb.FailureReasonNoRoute + return paymentsdb.FailureReasonNoRoute case errInsufficientBalance: - return channeldb.FailureReasonInsufficientBalance + return paymentsdb.FailureReasonInsufficientBalance default: - return channeldb.FailureReasonError + return paymentsdb.FailureReasonError } } diff --git a/routing/result_interpretation.go b/routing/result_interpretation.go index 55c4246f58f..713be73eabe 100644 --- a/routing/result_interpretation.go +++ b/routing/result_interpretation.go @@ -5,17 +5,17 @@ import ( "fmt" "io" - "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/lnwire" + paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/tlv" ) // Instantiate variables to allow taking a reference from the failure reason. var ( - reasonError = channeldb.FailureReasonError - reasonIncorrectDetails = channeldb.FailureReasonPaymentDetails + reasonError = paymentsdb.FailureReasonError + reasonIncorrectDetails = paymentsdb.FailureReasonPaymentDetails ) // pairResult contains the result of the interpretation of a payment attempt for @@ -70,7 +70,7 @@ type interpretedResult struct { // finalFailureReason is set to a non-nil value if it makes no more // sense to start another payment attempt. It will contain the reason // why. - finalFailureReason *channeldb.FailureReason + finalFailureReason *paymentsdb.FailureReason // policyFailure is set to a node pair if there is a policy failure on // that connection. This is used to control the second chance logic for diff --git a/routing/router.go b/routing/router.go index f7347c86f1b..1dac1085d49 100644 --- a/routing/router.go +++ b/routing/router.go @@ -15,7 +15,6 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/amp" - "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/graph/db/models" @@ -175,7 +174,7 @@ type MissionControlQuerier interface { // need to be made. ReportPaymentFail(attemptID uint64, rt *route.Route, failureSourceIdx *int, failure lnwire.FailureMessage) ( - *channeldb.FailureReason, error) + *paymentsdb.FailureReason, error) // ReportPaymentSuccess reports a successful payment to mission control // as input for future probability estimates. @@ -999,7 +998,7 @@ func (r *ChannelRouter) PreparePayment(payment *LightningPayment) ( // already in-flight. // // TODO(roasbeef): store records as part of creation info? - info := &channeldb.PaymentCreationInfo{ + info := &paymentsdb.PaymentCreationInfo{ PaymentIdentifier: payment.Identifier(), Value: payment.Amount, CreationTime: r.cfg.Clock.Now(), @@ -1038,7 +1037,7 @@ func (r *ChannelRouter) PreparePayment(payment *LightningPayment) ( // SendToRoute sends a payment using the provided route and fails the payment // when an error is returned from the attempt. func (r *ChannelRouter) SendToRoute(htlcHash lntypes.Hash, rt *route.Route, - firstHopCustomRecords lnwire.CustomRecords) (*channeldb.HTLCAttempt, + firstHopCustomRecords lnwire.CustomRecords) (*paymentsdb.HTLCAttempt, error) { return r.sendToRoute(htlcHash, rt, false, firstHopCustomRecords) @@ -1048,7 +1047,7 @@ func (r *ChannelRouter) SendToRoute(htlcHash lntypes.Hash, rt *route.Route, // the payment ONLY when a terminal error is returned from the attempt. func (r *ChannelRouter) SendToRouteSkipTempErr(htlcHash lntypes.Hash, rt *route.Route, - firstHopCustomRecords lnwire.CustomRecords) (*channeldb.HTLCAttempt, + firstHopCustomRecords lnwire.CustomRecords) (*paymentsdb.HTLCAttempt, error) { return r.sendToRoute(htlcHash, rt, true, firstHopCustomRecords) @@ -1062,13 +1061,13 @@ func (r *ChannelRouter) SendToRouteSkipTempErr(htlcHash lntypes.Hash, // the payment won't be failed unless a terminal error has occurred. func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route, skipTempErr bool, - firstHopCustomRecords lnwire.CustomRecords) (*channeldb.HTLCAttempt, + firstHopCustomRecords lnwire.CustomRecords) (*paymentsdb.HTLCAttempt, error) { // Helper function to fail a payment. It makes sure the payment is only // failed once so that the failure reason is not overwritten. failPayment := func(paymentIdentifier lntypes.Hash, - reason channeldb.FailureReason) error { + reason paymentsdb.FailureReason) error { payment, fetchErr := r.cfg.Control.FetchPayment( paymentIdentifier, @@ -1122,7 +1121,7 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route, // Record this payment hash with the ControlTower, ensuring it is not // already in-flight. - info := &channeldb.PaymentCreationInfo{ + info := &paymentsdb.PaymentCreationInfo{ PaymentIdentifier: paymentIdentifier, Value: amt, CreationTime: r.cfg.Clock.Now(), @@ -1191,7 +1190,7 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route, // Since for SendToRoute we won't retry in case the shard fails, we'll // mark the payment failed with the control tower immediately if the // skipTempErr is false. - reason := channeldb.FailureReasonError + reason := paymentsdb.FailureReasonError // If we failed to send the HTLC, we need to further decide if we want // to fail the payment. @@ -1448,7 +1447,7 @@ func (r *ChannelRouter) resumePayments() error { } // launchPayment is a helper closure that handles resuming the payment. - launchPayment := func(payment *channeldb.MPPayment) { + launchPayment := func(payment *paymentsdb.MPPayment) { defer r.wg.Done() // Get the hashes used for the outstanding HTLCs. @@ -1523,7 +1522,7 @@ func (r *ChannelRouter) resumePayments() error { // attempt to NOT be saved, resulting a payment being stuck forever. More info: // - https://github.com/lightningnetwork/lnd/issues/8146 // - https://github.com/lightningnetwork/lnd/pull/8174 -func (r *ChannelRouter) failStaleAttempt(a channeldb.HTLCAttempt, +func (r *ChannelRouter) failStaleAttempt(a paymentsdb.HTLCAttempt, payHash lntypes.Hash) { // We can only fail inflight HTLCs so we skip the settled/failed ones. @@ -1605,8 +1604,8 @@ func (r *ChannelRouter) failStaleAttempt(a channeldb.HTLCAttempt, // Fail the attempt in db. If there's an error, there's nothing we can // do here but logging it. - failInfo := &channeldb.HTLCFailInfo{ - Reason: channeldb.HTLCFailUnknown, + failInfo := &paymentsdb.HTLCFailInfo{ + Reason: paymentsdb.HTLCFailUnknown, FailTime: r.cfg.Clock.Now(), } _, err = r.cfg.Control.FailAttempt(payHash, a.AttemptID, failInfo) diff --git a/routing/router_test.go b/routing/router_test.go index d149c423e00..702c29db95e 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -21,7 +21,6 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/davecgh/go-spew/spew" sphinx "github.com/lightningnetwork/lightning-onion" - "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/graph" @@ -32,6 +31,7 @@ import ( "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" + paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/zpay32" @@ -1091,7 +1091,7 @@ func TestSendPaymentErrorPathPruning(t *testing.T) { // The final error returned should also indicate that the peer wasn't // online (the last error we returned). - require.Equal(t, channeldb.FailureReasonNoRoute, err) + require.Equal(t, paymentsdb.FailureReasonNoRoute, err) // Inspect the two attempts that were made before the payment failed. p, err := ctx.router.cfg.Control.FetchPayment(*payment.paymentHash) @@ -2429,7 +2429,7 @@ func TestSendToRouteSkipTempErrPermanentFailure(t *testing.T) { mock.Anything, mock.Anything, mock.Anything, ).Return(permErr) - failureReason := channeldb.FailureReasonPaymentDetails + failureReason := paymentsdb.FailureReasonPaymentDetails missionControl.On("ReportPaymentFail", mock.Anything, rt, mock.Anything, mock.Anything, ).Return(&failureReason, nil) diff --git a/rpcserver.go b/rpcserver.go index c922b23e4fd..8b4c1b1b566 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -71,6 +71,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/chanfunding" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/macaroons" + paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/lightningnetwork/lnd/peer" "github.com/lightningnetwork/lnd/peernotifier" "github.com/lightningnetwork/lnd/record" @@ -5892,7 +5893,7 @@ func (r *rpcServer) dispatchPaymentIntent( payment, ) } else { - var attempt *channeldb.HTLCAttempt + var attempt *paymentsdb.HTLCAttempt attempt, routerErr = r.server.chanRouter.SendToRoute( payIntent.rHash, payIntent.route, nil, ) @@ -7512,7 +7513,7 @@ func (r *rpcServer) ListPayments(ctx context.Context, } } - query := channeldb.PaymentsQuery{ + query := paymentsdb.Query{ IndexOffset: req.IndexOffset, MaxPayments: req.MaxPayments, Reversed: req.Reversed, diff --git a/server.go b/server.go index f0a618a1b5b..862ca6e5986 100644 --- a/server.go +++ b/server.go @@ -65,6 +65,7 @@ import ( "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/nat" "github.com/lightningnetwork/lnd/netann" + paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/lightningnetwork/lnd/peer" "github.com/lightningnetwork/lnd/peernotifier" "github.com/lightningnetwork/lnd/pool" @@ -339,7 +340,7 @@ type server struct { // payments. // // TODO(ziggie): Replace with interface. - kvPaymentsDB *channeldb.KVPaymentsDB + kvPaymentsDB *paymentsdb.KVPaymentsDB aliasMgr *aliasmgr.Manager