From 03af9858d2cb34e96d4543f0a2baa7e93bc3f083 Mon Sep 17 00:00:00 2001 From: ziggie Date: Wed, 13 Aug 2025 09:19:20 +0200 Subject: [PATCH] multi: move payment related code into own package This commit moves most of the code into its own package. It is the smallest code move possible without moving import cycles and keeping the changes to the code base as small as possible during refactor. --- channeldb/codec.go | 10 - channeldb/db.go | 13 +- channeldb/payments.go | 68 --- channeldb/payments_test.go | 474 ---------------- config_builder.go | 4 +- lnrpc/routerrpc/router_backend.go | 25 +- lnrpc/routerrpc/router_server.go | 9 +- lnrpc/routerrpc/router_server_test.go | 17 +- payments/db/codec.go | 141 +++++ .../db/kv_duplicate_payments.go | 14 +- .../db/kv_store.go | 80 +-- .../db/kv_store_test.go | 164 ++---- .../mp_payment.go => payments/db/payment.go | 104 +++- {channeldb => payments/db}/payment_status.go | 24 +- .../db}/payment_status_test.go | 26 +- .../db/payment_test.go | 511 +++++++++++++++++- payments/db/setup_test.go | 11 + payments/db/test_kvdb.go | 23 + routing/control_tower.go | 43 +- routing/control_tower_test.go | 60 +- routing/mock_test.go | 66 +-- routing/payment_lifecycle.go | 47 +- routing/payment_lifecycle_test.go | 67 +-- routing/router.go | 14 +- rpcserver.go | 5 +- server.go | 3 +- 26 files changed, 1075 insertions(+), 948 deletions(-) delete mode 100644 channeldb/payments_test.go create mode 100644 payments/db/codec.go rename channeldb/duplicate_payments.go => payments/db/kv_duplicate_payments.go (96%) rename channeldb/payments_kv_store.go => payments/db/kv_store.go (96%) rename channeldb/payments_kv_store_test.go => payments/db/kv_store_test.go (92%) rename channeldb/mp_payment.go => payments/db/payment.go (84%) rename {channeldb => payments/db}/payment_status.go (93%) rename {channeldb => payments/db}/payment_status_test.go (91%) rename channeldb/mp_payment_test.go => payments/db/payment_test.go (53%) create mode 100644 payments/db/setup_test.go create mode 100644 payments/db/test_kvdb.go diff --git a/channeldb/codec.go b/channeldb/codec.go index 95434a5f6..b82a25877 100644 --- a/channeldb/codec.go +++ b/channeldb/codec.go @@ -183,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 @@ -416,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 diff --git a/channeldb/db.go b/channeldb/db.go index 715b90686..00b29f65f 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/payments.go b/channeldb/payments.go index a23f891d0..3430e629c 100644 --- a/channeldb/payments.go +++ b/channeldb/payments.go @@ -92,71 +92,3 @@ 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 dce993ffe..000000000 --- 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 5a2406057..be56f962b 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 34cd7047e..b31d988a5 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -22,6 +22,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 +1489,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 +1530,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 +1538,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 +1744,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 +1814,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 +1827,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: diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 843222eee..10967d95a 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,17 @@ func (s *Server) trackPaymentStream(context context.Context, // No more payment updates. return nil } - result := item.(*channeldb.MPPayment) + result := item.(*paymentsdb.MPPayment) 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 bc5a7f16d..ce513d58c 100644 --- a/lnrpc/routerrpc/router_server_test.go +++ b/lnrpc/routerrpc/router_server_test.go @@ -10,6 +10,7 @@ import ( 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 +130,13 @@ func TestTrackPaymentsInflightUpdates(t *testing.T) { }() // Enqueue some payment updates on the mock. - towerMock.queue.ChanIn() <- &channeldb.MPPayment{ + towerMock.queue.ChanIn() <- &paymentsdb.MPPayment{ Info: &channeldb.PaymentCreationInfo{}, - Status: channeldb.StatusInFlight, + Status: paymentsdb.StatusInFlight, } - towerMock.queue.ChanIn() <- &channeldb.MPPayment{ + towerMock.queue.ChanIn() <- &paymentsdb.MPPayment{ Info: &channeldb.PaymentCreationInfo{}, - Status: channeldb.StatusSucceeded, + Status: paymentsdb.StatusSucceeded, } // Wait until there's 2 updates or the deadline is exceeded. @@ -191,13 +192,13 @@ func TestTrackPaymentsNoInflightUpdates(t *testing.T) { }() // Enqueue some payment updates on the mock. - towerMock.queue.ChanIn() <- &channeldb.MPPayment{ + towerMock.queue.ChanIn() <- &paymentsdb.MPPayment{ Info: &channeldb.PaymentCreationInfo{}, - Status: channeldb.StatusInFlight, + Status: paymentsdb.StatusInFlight, } - towerMock.queue.ChanIn() <- &channeldb.MPPayment{ + towerMock.queue.ChanIn() <- &paymentsdb.MPPayment{ Info: &channeldb.PaymentCreationInfo{}, - Status: channeldb.StatusSucceeded, + 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 000000000..997dab09c --- /dev/null +++ b/payments/db/codec.go @@ -0,0 +1,141 @@ +package paymentsdb + +import ( + "encoding/binary" + "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: + if _, ok := err.(UnknownElementType); !ok { + 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: + if _, ok := err.(UnknownElementType); !ok { + 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 96% rename from channeldb/duplicate_payments.go rename to payments/db/kv_duplicate_payments.go index 004722f00..9a41c920b 100644 --- a/channeldb/duplicate_payments.go +++ b/payments/db/kv_duplicate_payments.go @@ -1,4 +1,4 @@ -package channeldb +package paymentsdb import ( "bytes" @@ -8,10 +8,10 @@ import ( "time" "github.com/btcsuite/btcd/btcec/v2" + "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/routing/route" ) @@ -75,7 +75,7 @@ func fetchDuplicatePaymentStatus(bucket kvdb.RBucket) (PaymentStatus, error) { return StatusInFlight, nil } - return 0, paymentsdb.ErrPaymentNotInitiated + return 0, ErrPaymentNotInitiated } func deserializeDuplicateHTLCAttemptInfo(r io.Reader) ( @@ -94,11 +94,11 @@ func deserializeDuplicateHTLCAttemptInfo(r io.Reader) ( } func deserializeDuplicatePaymentCreationInfo(r io.Reader) ( - *PaymentCreationInfo, error) { + *channeldb.PaymentCreationInfo, error) { var scratch [8]byte - c := &PaymentCreationInfo{} + c := &channeldb.PaymentCreationInfo{} if _, err := io.ReadFull(r, c.PaymentIdentifier[:]); err != nil { return nil, err @@ -157,10 +157,10 @@ func fetchDuplicatePayment(bucket kvdb.RBucket) (*MPPayment, error) { } // Get failure reason if available. - var failureReason *FailureReason + var failureReason *channeldb.FailureReason b = bucket.Get(duplicatePaymentFailInfoKey) if b != nil { - reason := FailureReason(b[0]) + reason := channeldb.FailureReason(b[0]) failureReason = &reason } diff --git a/channeldb/payments_kv_store.go b/payments/db/kv_store.go similarity index 96% rename from channeldb/payments_kv_store.go rename to payments/db/kv_store.go index d18616cb0..0bd6576d0 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" @@ -131,15 +131,15 @@ type KVPaymentsDB struct { } // 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 { @@ -186,7 +186,7 @@ func initKVStore(db kvdb.Backend) error { // method returns successfully, the payment is guaranteed to be in the InFlight // state. func (p *KVPaymentsDB) InitPayment(paymentHash lntypes.Hash, - info *PaymentCreationInfo) error { + info *channeldb.PaymentCreationInfo) error { // Obtain a new sequence number for this payment. This is used // to sort the payments in order of creation, and also acts as @@ -229,7 +229,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 +403,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 +412,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 +424,7 @@ func (p *KVPaymentsDB) RegisterAttempt(paymentHash lntypes.Hash, h.Route.FinalHop().TotalAmtMsat { //nolint:ll - return paymentsdb.ErrBlindedPaymentTotalAmountMismatch + return ErrBlindedPaymentTotalAmountMismatch } continue @@ -434,12 +434,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 +448,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 +461,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 +574,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. @@ -605,7 +605,7 @@ func (p *KVPaymentsDB) updateHtlcKey(paymentHash lntypes.Hash, // its next call for this payment hash, allowing the switch to make a // subsequent payment. func (p *KVPaymentsDB) Fail(paymentHash lntypes.Hash, - reason FailureReason) (*MPPayment, error) { + reason channeldb.FailureReason) (*MPPayment, error) { var ( updateErr error @@ -619,8 +619,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 +631,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 +725,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 +743,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 +805,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) @@ -943,7 +943,7 @@ func (p *KVPaymentsDB) FetchPayments() ([]*MPPayment, error) { return payments, nil } -func fetchCreationInfo(bucket kvdb.RBucket) (*PaymentCreationInfo, error) { +func fetchCreationInfo(bucket kvdb.RBucket) (*channeldb.PaymentCreationInfo, error) { b := bucket.Get(paymentCreationInfoKey) if b == nil { return nil, fmt.Errorf("creation info not found") @@ -979,10 +979,10 @@ func fetchPayment(bucket kvdb.RBucket) (*MPPayment, error) { } // Get failure reason if available. - var failureReason *FailureReason + var failureReason *channeldb.FailureReason b := bucket.Get(paymentFailInfoKey) if b != nil { - reason := FailureReason(b[0]) + reason := channeldb.FailureReason(b[0]) failureReason = &reason } @@ -1051,7 +1051,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)) @@ -1205,7 +1205,7 @@ 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, ) @@ -1290,7 +1290,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 +1305,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 +1313,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 +1342,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 @@ -1621,7 +1621,7 @@ func fetchSequenceNumbers(paymentBucket kvdb.RBucket) ([][]byte, error) { return sequenceNumbers, nil } -func serializePaymentCreationInfo(w io.Writer, c *PaymentCreationInfo) error { +func serializePaymentCreationInfo(w io.Writer, c *channeldb.PaymentCreationInfo) error { var scratch [8]byte if _, err := w.Write(c.PaymentIdentifier[:]); err != nil { @@ -1658,12 +1658,12 @@ func serializePaymentCreationInfo(w io.Writer, c *PaymentCreationInfo) error { return nil } -func deserializePaymentCreationInfo(r io.Reader) (*PaymentCreationInfo, +func deserializePaymentCreationInfo(r io.Reader) (*channeldb.PaymentCreationInfo, error) { var scratch [8]byte - c := &PaymentCreationInfo{} + c := &channeldb.PaymentCreationInfo{} if _, err := io.ReadFull(r, c.PaymentIdentifier[:]); err != nil { return nil, err diff --git a/channeldb/payments_kv_store_test.go b/payments/db/kv_store_test.go similarity index 92% rename from channeldb/payments_kv_store_test.go rename to payments/db/kv_store_test.go index d1953fa95..2b5aaac41 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" @@ -13,10 +13,10 @@ import ( "github.com/btcsuite/btcwallet/walletdb" "github.com/davecgh/go-spew/spew" + "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" @@ -32,7 +32,7 @@ func genPreimage() ([32]byte, error) { return preimage, nil } -func genInfo(t *testing.T) (*PaymentCreationInfo, *HTLCAttemptInfo, +func genInfo(t *testing.T) (*channeldb.PaymentCreationInfo, *HTLCAttemptInfo, lntypes.Preimage, error) { preimage, err := genPreimage() @@ -50,7 +50,7 @@ func genInfo(t *testing.T) (*PaymentCreationInfo, *HTLCAttemptInfo, ) require.NoError(t, err) - return &PaymentCreationInfo{ + return &channeldb.PaymentCreationInfo{ PaymentIdentifier: rhash, Value: testRoute.ReceiverAmt(), CreationTime: time.Unix(time.Now().Unix(), 0), @@ -64,11 +64,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") @@ -86,7 +82,7 @@ func TestKVPaymentsDBSwitchFail(t *testing.T) { ) // Fail the payment, which should moved it to Failed. - failReason := FailureReasonNoRoute + failReason := channeldb.FailureReasonNoRoute _, err = paymentDB.Fail(info.PaymentIdentifier, failReason) require.NoError(t, err, "unable to fail payment hash") @@ -195,7 +191,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 +201,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 +223,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 +241,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 +264,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 +274,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 +286,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 +294,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, channeldb.FailureReasonNoRoute, + ) + require.ErrorIs(t, err, ErrPaymentNotInitiated) } // TestKVPaymentsDBDeleteNonInFlight checks that calling DeletePayments only @@ -325,17 +311,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 @@ -401,7 +383,7 @@ func TestKVPaymentsDBDeleteNonInFlight(t *testing.T) { } // Fail the payment, which should moved it to Failed. - failReason := FailureReasonNoRoute + failReason := channeldb.FailureReasonNoRoute _, err = paymentDB.Fail( info.PaymentIdentifier, failReason, ) @@ -531,7 +513,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 +531,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 +589,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 +690,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 +753,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] @@ -819,7 +787,7 @@ func TestKVPaymentsDBMultiShard(t *testing.T) { HTLCAttemptInfo: a, } - var firstFailReason *FailureReason + var firstFailReason *channeldb.FailureReason if test.settleFirst { _, err := paymentDB.SettleAttempt( info.PaymentIdentifier, a.AttemptID, @@ -859,7 +827,7 @@ func TestKVPaymentsDBMultiShard(t *testing.T) { // We also record a payment level fail, to move it into // a terminal state. - failReason := FailureReasonNoRoute + failReason := channeldb.FailureReasonNoRoute _, err = paymentDB.Fail( info.PaymentIdentifier, failReason, ) @@ -886,11 +854,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, ) } @@ -942,7 +910,7 @@ func TestKVPaymentsDBMultiShard(t *testing.T) { // failure. This is to allow multiple concurrent shard // write a terminal failure to the database without // syncing. - failReason := FailureReasonPaymentDetails + failReason := channeldb.FailureReasonPaymentDetails _, err = paymentDB.Fail( info.PaymentIdentifier, failReason, ) @@ -960,21 +928,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( @@ -1000,11 +968,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 +995,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 +1031,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 +1048,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 +1134,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 +1156,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 *channeldb.PaymentCreationInfo, f *channeldb.FailureReason, + a *htlcStatus) { t.Helper() @@ -1277,7 +1235,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 +1271,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, @@ -1381,7 +1339,7 @@ func createTestPayments(t *testing.T, p *KVPaymentsDB, payments []*payment) { ) require.NoError(t, err, "unable to fail htlc") - failReason := FailureReasonNoRoute + failReason := channeldb.FailureReasonNoRoute _, err = p.Fail(info.PaymentIdentifier, failReason) require.NoError(t, err, "unable to fail payment hash") @@ -1439,13 +1397,15 @@ func assertPayments(t *testing.T, paymentDB *KVPaymentsDB, require.Equal(t, payments, p) } -func makeFakeInfo(t *testing.T) (*PaymentCreationInfo, *HTLCAttemptInfo) { +func makeFakeInfo(t *testing.T) (*channeldb.PaymentCreationInfo, + *HTLCAttemptInfo) { + var preimg lntypes.Preimage copy(preimg[:], rev[:]) hash := preimg.Hash() - c := &PaymentCreationInfo{ + c := &channeldb.PaymentCreationInfo{ PaymentIdentifier: hash, Value: 1000, // Use single second precision to avoid false positive test @@ -1552,7 +1512,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 +1547,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 +1590,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,14 +1632,14 @@ 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, }, } @@ -1692,7 +1648,7 @@ func TestFetchPaymentWithSequenceNumber(t *testing.T) { t.Run(test.name, func(t *testing.T) { 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/channeldb/mp_payment.go b/payments/db/payment.go similarity index 84% rename from channeldb/mp_payment.go rename to payments/db/payment.go index f4467b743..a43e87144 100644 --- a/channeldb/mp_payment.go +++ b/payments/db/payment.go @@ -1,4 +1,4 @@ -package channeldb +package paymentsdb import ( "bytes" @@ -12,13 +12,81 @@ 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/lntypes" "github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnwire" - paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/lightningnetwork/lnd/routing/route" ) +// 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 +} + // 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,7 +239,7 @@ const ( // reason. HTLCFailUnknown HTLCFailReason = 0 - // HTLCFailUnknown is recorded for htlcs that had a failure message that + // HTLCFailUnreadable is recorded for htlcs that had a failure message that // couldn't be decrypted. HTLCFailUnreadable HTLCFailReason = 1 @@ -241,7 +309,7 @@ type MPPayment struct { // Info holds all static information about this payment, and is // populated when the payment is initiated. - Info *PaymentCreationInfo + Info *channeldb.PaymentCreationInfo // HTLCs holds the information about individual HTLCs that we send in // order to make the payment. @@ -252,7 +320,7 @@ type MPPayment struct { // // NOTE: Will only be set once the daemon has given up on the payment // altogether. - FailureReason *FailureReason + FailureReason *channeldb.FailureReason // Status is the current PaymentStatus of this payment. Status PaymentStatus @@ -273,7 +341,7 @@ func (m *MPPayment) Terminated() bool { // TerminalInfo returns any HTLC settle info recorded. If no settle info is // recorded, any payment level failure will be returned. If neither a settle // nor a failure is recorded, both return values will be nil. -func (m *MPPayment) TerminalInfo() (*HTLCAttempt, *FailureReason) { +func (m *MPPayment) TerminalInfo() (*HTLCAttempt, *channeldb.FailureReason) { for _, h := range m.HTLCs { if h.Settle != nil { return &h, nil @@ -350,12 +418,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 +441,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 +520,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 +531,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 +542,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 +564,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 +578,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 +600,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 +617,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. 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 179e22fc2..b42bb0eac 100644 --- a/channeldb/payment_status.go +++ b/payments/db/payment_status.go @@ -1,9 +1,9 @@ -package channeldb +package paymentsdb import ( "fmt" - paymentsdb "github.com/lightningnetwork/lnd/payments/db" + "github.com/lightningnetwork/lnd/channeldb" ) // PaymentStatus represent current status of payment. @@ -62,24 +62,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 +96,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 +108,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 +127,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) } } @@ -173,7 +173,7 @@ func (ps PaymentStatus) updatable() error { // failed` is false, this indicates all the payment's HTLCs have occurred a // temporarily failure and the payment is still in-flight. func decidePaymentStatus(htlcs []HTLCAttempt, - reason *FailureReason) (PaymentStatus, error) { + reason *channeldb.FailureReason) (PaymentStatus, error) { var ( inflight bool diff --git a/channeldb/payment_status_test.go b/payments/db/payment_status_test.go similarity index 91% rename from channeldb/payment_status_test.go rename to payments/db/payment_status_test.go index bd6181870..0fcd12100 100644 --- a/channeldb/payment_status_test.go +++ b/payments/db/payment_status_test.go @@ -1,11 +1,11 @@ -package channeldb +package paymentsdb import ( "fmt" "testing" + "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lntypes" - paymentsdb "github.com/lightningnetwork/lnd/payments/db" "github.com/stretchr/testify/require" ) @@ -24,13 +24,13 @@ func TestDecidePaymentStatus(t *testing.T) { } // Create a test failure reason and get the pointer. - reason := FailureReasonNoRoute + reason := channeldb.FailureReasonNoRoute failure := &reason testCases := []struct { name string htlcs []HTLCAttempt - reason *FailureReason + reason *channeldb.FailureReason expectedStatus PaymentStatus expectedErr error }{ @@ -198,33 +198,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 53% rename from channeldb/mp_payment_test.go rename to payments/db/payment_test.go index 455a04de0..897f570f8 100644 --- a/channeldb/mp_payment_test.go +++ b/payments/db/payment_test.go @@ -1,14 +1,21 @@ -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/channeldb" "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 +27,472 @@ 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, + } + + 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() + + 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 +537,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, }, } @@ -127,7 +598,7 @@ func TestPaymentSetState(t *testing.T) { // Create a test preimage and failure reason. preimage := lntypes.Preimage{1} - failureReasonError := FailureReasonError + failureReasonError := channeldb.FailureReasonError testCases := []struct { name string @@ -150,7 +621,7 @@ func TestPaymentSetState(t *testing.T) { }, }, totalAmt: 1, - errExpected: paymentsdb.ErrSentExceedsTotal, + errExpected: ErrSentExceedsTotal, }, { // Test that when the htlc is failed, the fee is not @@ -223,7 +694,7 @@ func TestPaymentSetState(t *testing.T) { t.Parallel() // Attach the payment info. - info := &PaymentCreationInfo{ + info := &channeldb.PaymentCreationInfo{ Value: lnwire.MilliSatoshi(tc.totalAmt), } tc.payment.Info = info @@ -295,7 +766,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 +781,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 +802,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, }, } @@ -353,7 +824,7 @@ func TestNeedWaitAttempts(t *testing.T) { tc := tc p := &MPPayment{ - Info: &PaymentCreationInfo{ + Info: &channeldb.PaymentCreationInfo{ PaymentIdentifier: [32]byte{1, 2, 3}, }, Status: tc.status, @@ -398,7 +869,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 +977,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 @@ -531,7 +1002,7 @@ func TestAllowMoreAttempts(t *testing.T) { tc := tc p := &MPPayment{ - Info: &PaymentCreationInfo{ + Info: &channeldb.PaymentCreationInfo{ PaymentIdentifier: [32]byte{1, 2, 3}, }, Status: tc.status, diff --git a/payments/db/setup_test.go b/payments/db/setup_test.go new file mode 100644 index 000000000..903eeca5e --- /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 000000000..25e236e48 --- /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 b2e4b7bd5..1a384cddc 100644 --- a/routing/control_tower.go +++ b/routing/control_tower.go @@ -6,6 +6,7 @@ import ( "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 +14,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 +39,7 @@ type DBMPPayment interface { // TerminalInfo returns the settled HTLC attempt or the payment's // failure reason. - TerminalInfo() (*channeldb.HTLCAttempt, *channeldb.FailureReason) + TerminalInfo() (*paymentsdb.HTLCAttempt, *channeldb.FailureReason) } // ControlTower tracks all outgoing payments made, whose primary purpose is to @@ -57,7 +58,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 +68,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. @@ -87,7 +88,7 @@ type ControlTower interface { FailPayment(lntypes.Hash, channeldb.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 +152,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 +169,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( @@ -215,7 +216,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 +236,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 +255,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) @@ -303,7 +304,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 +389,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 f45c2de38..c9ddf8c79 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,19 @@ 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) + result = item.(*paymentsdb.MPPayment) case <-time.After(testTimeout): t.Fatal("timeout waiting for payment result") } } - require.Equalf(t, channeldb.StatusSucceeded, result.GetStatus(), + require.Equalf(t, paymentsdb.StatusSucceeded, result.GetStatus(), "subscriber %v failed, want %s, got %s", i, - channeldb.StatusSucceeded, result.GetStatus()) + paymentsdb.StatusSucceeded, result.GetStatus()) attempt, _ := result.TerminalInfo() if attempt.Settle.Preimage != preimg { @@ -192,7 +192,7 @@ func TestKVPaymentsDBSubscribeAllSuccess(t *testing.T) { db := initDB(t) - paymentDB, err := channeldb.NewKVPaymentsDB( + paymentDB, err := paymentsdb.NewKVPaymentsDB( db, paymentsdb.WithKeepFailedPaymentAttempts(true), ) @@ -228,7 +228,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 +241,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 +255,14 @@ 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) + id := item.(*paymentsdb.MPPayment).Info.PaymentIdentifier + results[id] = item.(*paymentsdb.MPPayment) case <-time.After(testTimeout): require.Fail(t, "timeout waiting for payment result") } @@ -270,7 +270,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 +288,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 +311,7 @@ func TestKVPaymentsDBSubscribeAllImmediate(t *testing.T) { db := initDB(t) - paymentDB, err := channeldb.NewKVPaymentsDB( + paymentDB, err := paymentsdb.NewKVPaymentsDB( db, paymentsdb.WithKeepFailedPaymentAttempts(true), ) @@ -339,7 +339,7 @@ func TestKVPaymentsDBSubscribeAllImmediate(t *testing.T) { require.NotNil(t, update) require.Equal( t, info.PaymentIdentifier, - update.(*channeldb.MPPayment).Info.PaymentIdentifier, + update.(*paymentsdb.MPPayment).Info.PaymentIdentifier, ) require.Len(t, subscription.Updates(), 0) case <-time.After(testTimeout): @@ -354,7 +354,7 @@ func TestKVPaymentsDBUnsubscribeSuccess(t *testing.T) { db := initDB(t) - paymentDB, err := channeldb.NewKVPaymentsDB( + paymentDB, err := paymentsdb.NewKVPaymentsDB( db, paymentsdb.WithKeepFailedPaymentAttempts(true), ) @@ -411,8 +411,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 +429,7 @@ func testKVPaymentsDBSubscribeFail(t *testing.T, registerAttempt, db := initDB(t) - paymentDB, err := channeldb.NewKVPaymentsDB( + paymentDB, err := paymentsdb.NewKVPaymentsDB( db, paymentsdb.WithKeepFailedPaymentAttempts( keepFailedPaymentAttempts, @@ -465,8 +465,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, @@ -498,17 +498,17 @@ 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) + result = item.(*paymentsdb.MPPayment) 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,9 +533,9 @@ 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 { t.Fatal("unexpected failure reason") @@ -559,7 +559,7 @@ func initDB(t *testing.T) *channeldb.DB { ) } -func genInfo() (*channeldb.PaymentCreationInfo, *channeldb.HTLCAttemptInfo, +func genInfo() (*channeldb.PaymentCreationInfo, *paymentsdb.HTLCAttemptInfo, lntypes.Preimage, error) { preimage, err := genPreimage() @@ -572,7 +572,7 @@ 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 { diff --git a/routing/mock_test.go b/routing/mock_test.go index d9528254e..bb800a44d 100644 --- a/routing/mock_test.go +++ b/routing/mock_test.go @@ -252,7 +252,7 @@ type initArgs struct { } type registerAttemptArgs struct { - a *channeldb.HTLCAttemptInfo + a *paymentsdb.HTLCAttemptInfo } type settleAttemptArgs struct { @@ -260,7 +260,7 @@ type settleAttemptArgs struct { } type failAttemptArgs struct { - reason *channeldb.HTLCFailInfo + reason *paymentsdb.HTLCFailInfo } type failPaymentArgs struct { @@ -269,7 +269,7 @@ type failPaymentArgs struct { type testPayment struct { info channeldb.PaymentCreationInfo - attempts []channeldb.HTLCAttempt + attempts []paymentsdb.HTLCAttempt } type mockControlTowerOld struct { @@ -355,7 +355,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 +400,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 +409,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 +442,7 @@ 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,7 @@ func (m *mockControlTowerOld) FailAttempt(phash lntypes.Hash, pid uint64, } p.attempts[i].Failure = failInfo - return &channeldb.HTLCAttempt{ + return &paymentsdb.HTLCAttempt{ Failure: failInfo, }, nil } @@ -518,14 +518,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 +545,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 +555,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 @@ -745,15 +745,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 +762,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,7 +775,7 @@ 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, @@ -800,10 +800,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 +826,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 +847,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,19 +862,19 @@ func (m *mockMPPayment) AllowMoreAttempts() (bool, error) { return args.Bool(0), args.Error(1) } -func (m *mockMPPayment) TerminalInfo() (*channeldb.HTLCAttempt, +func (m *mockMPPayment) TerminalInfo() (*paymentsdb.HTLCAttempt, *channeldb.FailureReason) { args := m.Called() var ( - settleInfo *channeldb.HTLCAttempt + settleInfo *paymentsdb.HTLCAttempt failureInfo *channeldb.FailureReason ) settle := args.Get(0) if settle != nil { - settleInfo = settle.(*channeldb.HTLCAttempt) + settleInfo = settle.(*paymentsdb.HTLCAttempt) } reason := args.Get(1) diff --git a/routing/payment_lifecycle.go b/routing/payment_lifecycle.go index a42b21de5..e0a620133 100644 --- a/routing/payment_lifecycle.go +++ b/routing/payment_lifecycle.go @@ -16,6 +16,7 @@ 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" "github.com/lightningnetwork/lnd/routing/shards" "github.com/lightningnetwork/lnd/tlv" @@ -29,7 +30,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 +60,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. @@ -386,7 +387,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 +456,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 +505,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 +582,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 +609,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 +649,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 +658,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, @@ -823,7 +824,7 @@ 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 @@ -1030,34 +1031,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 +1083,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 +1135,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 +1155,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 +1180,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 +1205,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 a4f455039..6effccd29 100644 --- a/routing/payment_lifecycle_test.go +++ b/routing/payment_lifecycle_test.go @@ -15,6 +15,7 @@ import ( "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 +117,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 +148,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 +259,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 +272,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}, } @@ -378,7 +379,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 +416,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 +450,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, @@ -498,7 +499,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 +820,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. @@ -875,7 +876,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,14 +911,14 @@ 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. @@ -963,7 +964,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 +972,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 +1002,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 +1010,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 +1048,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 +1056,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 +1108,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 +1116,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() @@ -1199,7 +1200,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 +1208,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 +1301,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 +1309,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() @@ -1833,7 +1834,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/router.go b/routing/router.go index f7347c86f..8bb64c907 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1038,7 +1038,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 +1048,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,7 +1062,7 @@ 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 @@ -1448,7 +1448,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 +1523,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 +1605,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/rpcserver.go b/rpcserver.go index c922b23e4..d26e4b889 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.PaymentsQuery{ IndexOffset: req.IndexOffset, MaxPayments: req.MaxPayments, Reversed: req.Reversed, diff --git a/server.go b/server.go index f0a618a1b..862ca6e59 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