multi: introduce interface for payment database

This commit is contained in:
ziggie
2025-08-12 18:14:09 +02:00
parent 9a4968656a
commit 9f824fe1ee
5 changed files with 103 additions and 14 deletions

View File

@@ -925,9 +925,9 @@ type DatabaseInstances struct {
// InvoiceDB is the database that stores information about invoices.
InvoiceDB invoices.InvoiceDB
// KVPaymentsDB is the database that stores all payment related
// PaymentsDB is the database that stores all payment related
// information.
KVPaymentsDB *paymentsdb.KVPaymentsDB
PaymentsDB paymentsdb.DB
// MacaroonDB is the database that stores macaroon root keys.
MacaroonDB kvdb.Backend
@@ -1237,7 +1237,7 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
return nil, nil, err
}
dbs.KVPaymentsDB = kvPaymentsDB
dbs.PaymentsDB = kvPaymentsDB
// Wrap the watchtower client DB and make sure we clean up.
if cfg.WtClient.Active {

91
payments/db/interface.go Normal file
View File

@@ -0,0 +1,91 @@
package paymentsdb
import (
"context"
"github.com/lightningnetwork/lnd/lntypes"
)
// DB represents the interface to the underlying payments database.
type DB interface {
PaymentReader
PaymentWriter
}
// PaymentReader represents the interface to read operations from the payments
// database.
type PaymentReader interface {
// QueryPayments queries the payments database and should support
// pagination.
QueryPayments(ctx context.Context, query Query) (Response, error)
// FetchPayment fetches the payment corresponding to the given payment
// hash.
FetchPayment(paymentHash lntypes.Hash) (*MPPayment, error)
// FetchInFlightPayments returns all payments with status InFlight.
FetchInFlightPayments() ([]*MPPayment, error)
}
// PaymentWriter represents the interface to write operations to the payments
// database.
type PaymentWriter interface {
// DeletePayment deletes a payment from the DB given its payment hash.
DeletePayment(paymentHash lntypes.Hash, failedAttemptsOnly bool) error
// DeletePayments deletes all payments from the DB given the specified
// flags.
DeletePayments(failedOnly, failedAttemptsOnly bool) (int, error)
PaymentControl
}
// PaymentControl represents the interface to control the payment lifecycle and
// its database operations. This interface represents the control flow of how
// a payment should be handled in the database. They are not just writing
// operations but they inherently represent the flow of a payment. The methods
// are called in the following order:
//
// 1. InitPayment
// 2. RegisterAttempt (a payment can have multiple attempts)
// 3. SettleAttempt or FailAttempt (attempts can also fail as long as the
// sending amount will be eventually settled).
// 4. Payment succeeds or "Fail" is called.
// 5. DeleteFailedAttempts is called which will delete all failed attempts
// for a payment to clean up the database.
type PaymentControl interface {
// InitPayment checks that no other payment with the same payment hash
// exists in the database before creating a new payment. However, it
// should allow the user making a subsequent payment if the payment is
// in a Failed state.
InitPayment(lntypes.Hash, *PaymentCreationInfo) error
// RegisterAttempt atomically records the provided HTLCAttemptInfo.
RegisterAttempt(lntypes.Hash, *HTLCAttemptInfo) (*MPPayment, error)
// SettleAttempt marks the given attempt settled with the preimage. If
// this is a multi shard payment, this might implicitly mean the
// full payment succeeded.
//
// After invoking this method, InitPayment should always return an
// 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, *HTLCSettleInfo) (*MPPayment, error)
// FailAttempt marks the given payment attempt failed.
FailAttempt(lntypes.Hash, uint64, *HTLCFailInfo) (*MPPayment, error)
// Fail transitions a payment into the Failed state, and records
// the ultimate reason the payment failed. Note that this should only
// be called when all active attempts are already failed. After
// invoking this method, InitPayment should return nil on its next call
// for this payment hash, allowing the user to make a subsequent
// payment.
Fail(lntypes.Hash, FailureReason) (*MPPayment, error)
// DeleteFailedAttempts removes all failed HTLCs from the db. It should
// be called for a given payment whenever all inflight htlcs are
// completed, and the payment has reached a final terminal state.
DeleteFailedAttempts(lntypes.Hash) error
}

View File

@@ -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 *paymentsdb.KVPaymentsDB
db paymentsdb.DB
// 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 *paymentsdb.KVPaymentsDB) ControlTower {
func NewControlTower(db paymentsdb.DB) ControlTower {
return &controlTower{
db: db,
subscribersAllPayments: make(

View File

@@ -7529,7 +7529,7 @@ func (r *rpcServer) ListPayments(ctx context.Context,
query.MaxPayments = math.MaxUint64
}
paymentsQuerySlice, err := r.server.kvPaymentsDB.QueryPayments(
paymentsQuerySlice, err := r.server.paymentsDB.QueryPayments(
ctx, query,
)
if err != nil {
@@ -7612,7 +7612,7 @@ func (r *rpcServer) DeletePayment(ctx context.Context,
rpcsLog.Infof("[DeletePayment] payment_identifier=%v, "+
"failed_htlcs_only=%v", hash, req.FailedHtlcsOnly)
err = r.server.kvPaymentsDB.DeletePayment(hash, req.FailedHtlcsOnly)
err = r.server.paymentsDB.DeletePayment(hash, req.FailedHtlcsOnly)
if err != nil {
return nil, err
}
@@ -7652,7 +7652,7 @@ func (r *rpcServer) DeleteAllPayments(ctx context.Context,
"failed_htlcs_only=%v", req.FailedPaymentsOnly,
req.FailedHtlcsOnly)
numDeletedPayments, err := r.server.kvPaymentsDB.DeletePayments(
numDeletedPayments, err := r.server.paymentsDB.DeletePayments(
req.FailedPaymentsOnly, req.FailedHtlcsOnly,
)
if err != nil {

View File

@@ -336,11 +336,9 @@ type server struct {
invoicesDB invoices.InvoiceDB
// kvPaymentsDB is the DB that contains all functions for managing
// paymentsDB is the DB that contains all functions for managing
// payments.
//
// TODO(ziggie): Replace with interface.
kvPaymentsDB *paymentsdb.KVPaymentsDB
paymentsDB paymentsdb.DB
aliasMgr *aliasmgr.Manager
@@ -683,7 +681,7 @@ func newServer(ctx context.Context, cfg *Config, listenAddrs []net.Addr,
addrSource: addrSource,
miscDB: dbs.ChanStateDB,
invoicesDB: dbs.InvoiceDB,
kvPaymentsDB: dbs.KVPaymentsDB,
paymentsDB: dbs.PaymentsDB,
cc: cc,
sigPool: lnwallet.NewSigPool(cfg.Workers.Sig, cc.Signer),
writePool: writePool,
@@ -1007,7 +1005,7 @@ func newServer(ctx context.Context, cfg *Config, listenAddrs []net.Addr,
PathFindingConfig: pathFindingConfig,
}
s.controlTower = routing.NewControlTower(dbs.KVPaymentsDB)
s.controlTower = routing.NewControlTower(dbs.PaymentsDB)
strictPruning := cfg.Bitcoin.Node == "neutrino" ||
cfg.Routing.StrictZombiePruning