Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
9362682
lnd+paymentsdb: introduce harness for the payment sql backend
ziggie1984 Sep 29, 2025
dd585a8
sqldb: add payment sql tables
ziggie1984 Sep 29, 2025
8c7b236
lnd: make the payment schema migration available for testing
ziggie1984 Sep 29, 2025
b6a05f7
docs: add release-notes
ziggie1984 Oct 12, 2025
e7a3096
sqldb: add index and comment to payment tables
ziggie1984 Oct 15, 2025
18e7768
multi: add relevant queries for QueryPayments implemenation
ziggie1984 Oct 12, 2025
eeca189
paymentsdb: add new internal error
ziggie1984 Oct 12, 2025
e8fe45f
paymentsdb: implement QueryPayments for sql backend
ziggie1984 Oct 12, 2025
8aae9ff
paymentsdb: implement FetchPayment for sql backend
ziggie1984 Oct 15, 2025
4c5eaad
docs: add release-notes
ziggie1984 Oct 12, 2025
f824174
paymentsdb: enhance some godoc function descriptions
ziggie1984 Nov 11, 2025
27071ac
sqldb: Change payment_intent relationship to payment table
ziggie1984 Nov 11, 2025
bdea68b
sqldb: add queries for deleting a payment and attempts
ziggie1984 Oct 15, 2025
6faf68c
paymentsdb: add query to only fetch resolution type for HTLCs
ziggie1984 Nov 13, 2025
3012eb2
paymentsdb: implement DeleteFailedAttempts for sql backend
ziggie1984 Oct 15, 2025
c3e25ff
paymentsdb: implement DeletePayment for sql backend
ziggie1984 Oct 15, 2025
0c96a2d
sqldb+paymentsdb: add queries to insert all relavant data
ziggie1984 Oct 15, 2025
510f6fb
paymentsdb: implement InitPayment for sql backend
ziggie1984 Oct 15, 2025
d2cdd9d
paymentsdb: add note to RegisterAttempt
ziggie1984 Nov 13, 2025
40c8502
paymentsdb: implement RegisterAttempt for sql backend
ziggie1984 Oct 15, 2025
c0e55b5
paymentsdb: verify total amount for last hop in the blinded path
ziggie1984 Nov 11, 2025
620dacc
paymentsdb: implement SettleAttempt for sql backend
ziggie1984 Oct 15, 2025
4c867c1
docs: add release-notes
ziggie1984 Oct 15, 2025
7d87c0c
paymentsdb: fix formatting for sql QueryPayments
ziggie1984 Nov 20, 2025
5d328c9
paymentsdb: remove pointer receiver dependecy to make it more robust
ziggie1984 Nov 20, 2025
caf73c5
paymentsdb: rename paymentsBatchData
ziggie1984 Nov 20, 2025
fd6796e
multi: implement Fail method for sql backend
ziggie1984 Oct 15, 2025
53a7877
paymentsdb: implement FailAttempt for sql backend
ziggie1984 Oct 15, 2025
ff12082
paymentsdb: implement DeletePayments for sql backend
ziggie1984 Oct 15, 2025
218a30d
paymentsdb: add a wrapper to the fetchpayment method
ziggie1984 Nov 13, 2025
b02531c
paymentsdb: rename functions and variables
ziggie1984 Nov 20, 2025
82ee78c
paymentsdb: use batch function when querying for resolutions
ziggie1984 Nov 20, 2025
969b00e
paymentsdb: implement FetchInFlightPayments for sql backend
ziggie1984 Oct 15, 2025
4e52896
paymentsdb: added unit test for computePaymentStatusFromResolutions
ziggie1984 Nov 21, 2025
c919d25
docs: add release-notes
ziggie1984 Nov 13, 2025
9ad3f21
paymentsdb: remove kvstore from sql db implementation
ziggie1984 Oct 15, 2025
fdfa143
paymentsdb: refactor test helpers
ziggie1984 Oct 15, 2025
de50f47
paymentsdb: make QueryPayments test db agnostic
ziggie1984 Oct 15, 2025
2eadbfb
paymentsdb: fix test case before testing sql backend
ziggie1984 Oct 15, 2025
570863c
paymentsdb: add harness to run payment db agnostic tests
ziggie1984 Oct 15, 2025
080fd78
paymentsdb: introduce a harness interface
ziggie1984 Nov 14, 2025
5f1f413
paymentsdb: make specific kv store tests only available via build tag
ziggie1984 Nov 14, 2025
f518931
itest: fix list_payments accuracy edge case
ziggie1984 Oct 15, 2025
ea4e183
lnrpc: fix linter
ziggie1984 Oct 16, 2025
bfd59aa
paymentsdb: add more comments
ziggie1984 Oct 17, 2025
e40c8df
paymentsdb: add firstcustom records to unit tests
ziggie1984 Oct 17, 2025
dfacb9e
paymentsdb: add unit test for FetchInflightPayments method
ziggie1984 Nov 24, 2025
fcb8929
docs: add release-notes
ziggie1984 Oct 16, 2025
bc15d4b
multi: thread context through DeletePayment
ziggie1984 Oct 20, 2025
563a2ac
multi: thread context through DeletePayments
ziggie1984 Oct 20, 2025
25d05be
multi: thread context through FetchPayment
ziggie1984 Oct 20, 2025
10062ca
multi: thread context through FetchInflightPayments
ziggie1984 Oct 20, 2025
6002649
multi: thread context through InitPayment
ziggie1984 Oct 20, 2025
8bb7150
multi: thread context through RegisterAttempt method
ziggie1984 Nov 11, 2025
3b7fdab
multi: thread context through SettleAttempt
ziggie1984 Oct 21, 2025
4463009
multi: thread context through FailAttempt
ziggie1984 Oct 21, 2025
635a67c
multi: thread context through Fail payment functions
ziggie1984 Oct 21, 2025
7f2df4b
multi: thread context through DeleteFailedAttempts
ziggie1984 Oct 21, 2025
33597ad
docs: add release notes
ziggie1984 Oct 21, 2025
dfe0c43
routing: Add context to requestRoute
ziggie1984 Oct 21, 2025
8038dc0
multi: thread context through payment lifecyle functions
ziggie1984 Oct 21, 2025
e130ccc
routing: Thread context through failPaymentAndAttempt
ziggie1984 Oct 21, 2025
5b06854
routing: add context to failAttempt
ziggie1984 Oct 21, 2025
03d891d
routing: add context to reloadInflightAttempts
ziggie1984 Oct 21, 2025
4cc5428
routing: add context to reloadPayment method
ziggie1984 Oct 21, 2025
d8d5d3f
multi: thread context through SendPayment
ziggie1984 Nov 17, 2025
9da39e5
docs: add release-notes
ziggie1984 Oct 21, 2025
3ae0ccc
paymentsdb: make delete payments test db agnostic
ziggie1984 Nov 15, 2025
253e4fd
routing: add TODO to also delete payments without HTLCs
ziggie1984 Nov 16, 2025
995ad7e
multi: move failed attempt cfg option to the router subsytem
ziggie1984 Nov 18, 2025
a861ac8
paymentsdb: refactor test helpers
ziggie1984 Nov 21, 2025
a0a22b7
paymentsdb: add additional test for first hop data
ziggie1984 Nov 24, 2025
372fbbd
paymentsdb: add more unit tests to increase coverage
ziggie1984 Nov 24, 2025
2548c15
docs: add release-notes
ziggie1984 Nov 24, 2025
a2cb753
sqldb: rename 000009_payments to 000010_payments
ziggie1984 Feb 15, 2026
0c2951a
paymentsdb: fix SettleAttempt and FailAttempt to use caller-provided …
ziggie1984 Feb 24, 2026
216de55
paymentsdb: sort FetchInFlightPayments result by sequence number
ziggie1984 Feb 24, 2026
e9a8826
paymentsdb: fix duplicate interface check and down migration drop order
ziggie1984 Feb 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 31 additions & 22 deletions config_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -1228,6 +1228,23 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(

return nil, nil, err
}

// Create the payments DB.
//
// NOTE: In the regular build, this will construct a kvdb
// backed payments backend. With the test_native_sql tag, it
// will build a SQL payments backend.
sqlPaymentsDB, err := d.getPaymentsStore(
baseDB, dbs.ChanStateDB.Backend,
)
if err != nil {
err = fmt.Errorf("unable to get payments store: %w",
err)

return nil, nil, err
}

dbs.PaymentsDB = sqlPaymentsDB
} else {
// Check if the invoice bucket tombstone is set. If it is, we
// need to return and ask the user switch back to using the
Expand Down Expand Up @@ -1256,40 +1273,32 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
if err != nil {
return nil, nil, err
}
}

dbs.GraphDB, err = graphdb.NewChannelGraph(graphStore, chanGraphOpts...)
if err != nil {
cleanUp()
// Create the payments DB.
kvPaymentsDB, err := paymentsdb.NewKVStore(
dbs.ChanStateDB,
)
if err != nil {
cleanUp()

err = fmt.Errorf("unable to open channel graph DB: %w", err)
d.logger.Error(err)
err = fmt.Errorf("unable to open payments DB: %w", err)
d.logger.Error(err)

return nil, nil, err
}
return nil, nil, err
}

// Mount the payments DB which is only KV for now.
//
// TODO(ziggie): Add support for SQL payments DB.
// Mount the payments DB for the KV store.
paymentsDBOptions := []paymentsdb.OptionModifier{
paymentsdb.WithKeepFailedPaymentAttempts(
cfg.KeepFailedPaymentAttempts,
),
dbs.PaymentsDB = kvPaymentsDB
}
kvPaymentsDB, err := paymentsdb.NewKVStore(
dbs.ChanStateDB,
paymentsDBOptions...,
)

dbs.GraphDB, err = graphdb.NewChannelGraph(graphStore, chanGraphOpts...)
if err != nil {
cleanUp()

err = fmt.Errorf("unable to open payments DB: %w", err)
err = fmt.Errorf("unable to open channel graph DB: %w", err)
d.logger.Error(err)

return nil, nil, err
}
dbs.PaymentsDB = kvPaymentsDB

// Wrap the watchtower client DB and make sure we clean up.
if cfg.WtClient.Active {
Expand Down
11 changes: 11 additions & 0 deletions config_prod.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"context"

"github.com/lightningnetwork/lnd/kvdb"
paymentsdb "github.com/lightningnetwork/lnd/payments/db"
"github.com/lightningnetwork/lnd/sqldb"
"github.com/lightningnetwork/lnd/sqldb/sqlc"
)

Expand All @@ -24,3 +26,12 @@ func (d *DefaultDatabaseBuilder) getSQLMigration(ctx context.Context,

return nil, false
}

// getPaymentsStore returns a paymentsdb.DB backed by a paymentsdb.KVStore
// implementation.
func (d *DefaultDatabaseBuilder) getPaymentsStore(_ *sqldb.BaseDB,
kvBackend kvdb.Backend,
opts ...paymentsdb.OptionModifier) (paymentsdb.DB, error) {

return paymentsdb.NewKVStore(kvBackend, opts...)
}
29 changes: 29 additions & 0 deletions config_test_native_sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ package lnd

import (
"context"
"database/sql"

"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lncfg"
paymentsdb "github.com/lightningnetwork/lnd/payments/db"
"github.com/lightningnetwork/lnd/sqldb"
"github.com/lightningnetwork/lnd/sqldb/sqlc"
)

Expand All @@ -25,3 +29,28 @@ func (d *DefaultDatabaseBuilder) getSQLMigration(_ context.Context,
return nil, false
}
}

// getPaymentsStore returns a paymentsdb.DB backed by a paymentsdb.SQLStore
// implementation.
func (d *DefaultDatabaseBuilder) getPaymentsStore(baseDB *sqldb.BaseDB,
kvBackend kvdb.Backend,
opts ...paymentsdb.OptionModifier) (paymentsdb.DB, error) {

paymentsExecutor := sqldb.NewTransactionExecutor(
baseDB, func(tx *sql.Tx) paymentsdb.SQLQueries {
return baseDB.WithTx(tx)
},
)

queryConfig := d.cfg.DB.Sqlite.QueryConfig
if d.cfg.DB.Backend == lncfg.PostgresBackend {
queryConfig = d.cfg.DB.Postgres.QueryConfig
}

return paymentsdb.NewSQLStore(
&paymentsdb.SQLStoreConfig{
QueryCfg: &queryConfig,
},
paymentsExecutor, opts...,
)
}
24 changes: 24 additions & 0 deletions docs/release-notes/release-notes-0.21.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,28 @@
[4](https://github.com/lightningnetwork/lnd/pull/10542),
[5](https://github.com/lightningnetwork/lnd/pull/10572).

* Payment Store SQL implementation and migration project:
* Introduce an [abstract payment
store](https://github.com/lightningnetwork/lnd/pull/10153) interface and
refacotor the payment related LND code to make it more modular.
* Implement the SQL backend for the [payments
database](https://github.com/lightningnetwork/lnd/pull/9147)
* Implement query methods (QueryPayments,FetchPayment) for the [payments db
SQL Backend](https://github.com/lightningnetwork/lnd/pull/10287)
* Implement insert methods for the [payments db
SQL Backend](https://github.com/lightningnetwork/lnd/pull/10291)
* Implement third(final) Part of SQL backend [payment
functions](https://github.com/lightningnetwork/lnd/pull/10368)
* Finalize SQL payments implementation [enabling unit and itests
for SQL backend](https://github.com/lightningnetwork/lnd/pull/10292)
* [Thread context through payment
db functions Part 1](https://github.com/lightningnetwork/lnd/pull/10307)
* [Thread context through payment
db functions Part 2](https://github.com/lightningnetwork/lnd/pull/10308)
* [Finalize SQL implementation for
payments db](https://github.com/lightningnetwork/lnd/pull/10373)


## Code Health

## Tooling and Documentation
Expand All @@ -207,8 +229,10 @@
* Boris Nagaev
* Elle Mouton
* Erick Cestari
* Gijs van Dam
* hieblmi
* Matt Morehouse
* Mohamed Awnallah
* Nishant Bansal
* Pins
* Ziggie
59 changes: 43 additions & 16 deletions itest/lnd_payment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,61 +504,86 @@ func testListPayments(ht *lntest.HarnessTest) {
expected bool
}

// Create test cases to check the timestamp filters.
createCases := func(createTimeSeconds uint64) []testCase {
// Create test cases with proper rounding for start and end dates.
createCases := func(startTimeSeconds,
endTimeSeconds uint64) []testCase {

return []testCase{
{
// Use a start date same as the creation date
// should return us the item.
// (truncated) should return us the item.
name: "exact start date",
startDate: createTimeSeconds,
startDate: startTimeSeconds,
expected: true,
},
{
// Use an earlier start date should return us
// the item.
name: "earlier start date",
startDate: createTimeSeconds - 1,
startDate: startTimeSeconds - 1,
expected: true,
},
{
// Use a future start date should return us
// nothing.
name: "future start date",
startDate: createTimeSeconds + 1,
startDate: startTimeSeconds + 1,
expected: false,
},
{
// Use an end date same as the creation date
// should return us the item.
// (ceiling) should return us the item.
name: "exact end date",
endDate: createTimeSeconds,
endDate: endTimeSeconds,
expected: true,
},
{
// Use an end date in the future should return
// us the item.
name: "future end date",
endDate: createTimeSeconds + 1,
endDate: endTimeSeconds + 1,
expected: true,
},
{
// Use an earlier end date should return us
// nothing.
name: "earlier end date",
endDate: createTimeSeconds - 1,
name: "earlier end date",
// The native sql backend has a higher
// precision than the kv backend, the native sql
// backend uses microseconds, the kv backend
// when filtering uses seconds so we need to
// subtract 2 seconds to ensure the payment is
// not included.
// We could also truncate before inserting
// into the sql db but I rather relax this test
// here.
endDate: endTimeSeconds - 2,
expected: false,
},
}
}

// Get the payment creation time in seconds.
paymentCreateSeconds := uint64(
p.CreationTimeNs / time.Second.Nanoseconds(),
// Get the payment creation time in seconds, using different approaches
// for start and end date comparisons to avoid rounding issues.
creationTime := time.Unix(0, p.CreationTimeNs)

// For start date comparisons: use truncation (floor) to include
// payments from the beginning of that second.
paymentCreateSecondsStart := uint64(
creationTime.Truncate(time.Second).Unix(),
)

// For end date comparisons: use ceiling to include payments up to the
// end of that second.
paymentCreateSecondsEnd := uint64(
(p.CreationTimeNs + time.Second.Nanoseconds() - 1) /
time.Second.Nanoseconds(),
)

// Create test cases from the payment creation time.
testCases := createCases(paymentCreateSeconds)
testCases := createCases(
paymentCreateSecondsStart, paymentCreateSecondsEnd,
)

// We now check the timestamp filters in `ListPayments`.
for _, tc := range testCases {
Expand All @@ -578,7 +603,9 @@ func testListPayments(ht *lntest.HarnessTest) {
}

// Create test cases from the invoice creation time.
testCases = createCases(uint64(invoice.CreationDate))
testCases = createCases(
uint64(invoice.CreationDate), uint64(invoice.CreationDate),
)

// We now do the same check for `ListInvoices`.
for _, tc := range testCases {
Expand Down
4 changes: 4 additions & 0 deletions lnrpc/routerrpc/router_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -1767,6 +1767,10 @@ func (r *RouterBackend) MarshallPayment(payment *paymentsdb.MPPayment) (
// If any of the htlcs have settled, extract a valid
// preimage.
if htlc.Settle != nil {
// For AMP payments all hashes will be different so we
// will only show the last htlc preimage, this is a
// current limitation for AMP payments because for
// MPP payments all hashes are the same.
preimage = htlc.Settle.Preimage
fee += htlc.Route.TotalFees()
}
Expand Down
4 changes: 2 additions & 2 deletions lnrpc/routerrpc/router_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1088,11 +1088,11 @@ func (s *Server) SendToRouteV2(ctx context.Context,
// db.
if req.SkipTempErr {
attempt, err = s.cfg.Router.SendToRouteSkipTempErr(
hash, route, firstHopRecords,
ctx, hash, route, firstHopRecords,
)
} else {
attempt, err = s.cfg.Router.SendToRoute(
hash, route, firstHopRecords,
ctx, hash, route, firstHopRecords,
)
}
if attempt != nil {
Expand Down
10 changes: 10 additions & 0 deletions payments/db/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ var (
ErrMixedBlindedAndNonBlindedPayments = errors.New("mixed blinded and " +
"non-blinded payments")

// ErrBlindedPaymentMissingTotalAmount is returned if we try to
// register a blinded payment attempt where the final hop doesn't set
// the total amount.
ErrBlindedPaymentMissingTotalAmount = errors.New("blinded payment " +
"final hop must set total amount")

// ErrMPPPaymentAddrMismatch is returned if we try to register an MPP
// shard where the payment address doesn't match existing shards.
ErrMPPPaymentAddrMismatch = errors.New("payment address mismatch")
Expand Down Expand Up @@ -136,4 +142,8 @@ var (
// NOTE: Only used for the kv backend.
ErrNoSequenceNrIndex = errors.New("payment sequence number index " +
"does not exist")

// errMaxPaymentsReached is used internally to signal that the maximum
// number of payments has been reached during a paginated query.
errMaxPaymentsReached = errors.New("max payments reached")
)
Loading
Loading