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