Merge pull request #9847 from ziggie1984/refactor-payments-code-04

Refactor Payment PR 4
This commit is contained in:
Yong authored and GitHub committed 2025-08-15 12:03:33 +08:00
commit dfc29b3f71
34 files changed
+1264 -1139

No files matched your search

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