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