sqldb+paymentsdb: add queries to insert all relavant data
What changed, and why it matters
This commit adds new database helper functions for storing payment-related records in LND's SQL backend. It is purely an infrastructure/CRUD addition: more INSERT statements and generated Go wrappers. There is no bug fix, no logic change, and no security-sensitive behavior visible in the diff.
No security action required. Review as normal code-quality/database-schema work.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch extends the payments SQL store with generated sqlc queries and interface methods to insert payments, payment intents, HTLC attempts, route hops, MPP/AMP/blinded hop metadata, custom records, and attempt resolutions (settle/fail). All queries are straightforward parameterized INSERTs. No input validation, access control, or business-logic changes are present. No security relevance is claimed by the commit message or code.
Changed components
payments/db/sql_store.gosqldb/sqlc/payments.sql.gosqldb/sqlc/querier.gosqldb/sqlc/queries/payments.sqlInspect captured patch +598 / −0
diff --git a/payments/db/sql_store.go b/payments/db/sql_store.go
index e415f63..90aada7 100644
--- a/payments/db/sql_store.go
+++ b/payments/db/sql_store.go
@@ -60,6 +60,20 @@ type SQLQueries interface {
/*
Payment DB write operations.
*/
+ InsertPaymentIntent(ctx context.Context, arg sqlc.InsertPaymentIntentParams) (int64, error)
+ InsertPayment(ctx context.Context, arg sqlc.InsertPaymentParams) error
+ InsertPaymentFirstHopCustomRecord(ctx context.Context, arg sqlc.InsertPaymentFirstHopCustomRecordParams) error
+
+ InsertHtlcAttempt(ctx context.Context, arg sqlc.InsertHtlcAttemptParams) (int64, error)
+ InsertRouteHop(ctx context.Context, arg sqlc.InsertRouteHopParams) (int64, error)
+ InsertRouteHopMpp(ctx context.Context, arg sqlc.InsertRouteHopMppParams) error
+ InsertRouteHopAmp(ctx context.Context, arg sqlc.InsertRouteHopAmpParams) error
+ InsertRouteHopBlinded(ctx context.Context, arg sqlc.InsertRouteHopBlindedParams) error
+
+ InsertPaymentAttemptFirstHopCustomRecord(ctx context.Context, arg sqlc.InsertPaymentAttemptFirstHopCustomRecordParams) error
+ InsertPaymentHopCustomRecord(ctx context.Context, arg sqlc.InsertPaymentHopCustomRecordParams) error
+
+ SettleAttempt(ctx context.Context, arg sqlc.SettleAttemptParams) error
DeletePayment(ctx context.Context, paymentID int64) error
diff --git a/sqldb/sqlc/payments.sql.go b/sqldb/sqlc/payments.sql.go
index 0883023..3b6f6e2 100644
--- a/sqldb/sqlc/payments.sql.go
+++ b/sqldb/sqlc/payments.sql.go
@@ -45,6 +45,46 @@ func (q *Queries) DeletePayment(ctx context.Context, id int64) error {
return err
}
+const failAttempt = `-- name: FailAttempt :exec
+INSERT INTO payment_htlc_attempt_resolutions (
+ attempt_index,
+ resolution_time,
+ resolution_type,
+ failure_source_index,
+ htlc_fail_reason,
+ failure_msg
+)
+VALUES (
+ $1,
+ $2,
+ $3,
+ $4,
+ $5,
+ $6
+)
+`
+
+type FailAttemptParams struct {
+ AttemptIndex int64
+ ResolutionTime time.Time
+ ResolutionType int32
+ FailureSourceIndex sql.NullInt32
+ HtlcFailReason sql.NullInt32
+ FailureMsg []byte
+}
+
+func (q *Queries) FailAttempt(ctx context.Context, arg FailAttemptParams) error {
+ _, err := q.db.ExecContext(ctx, failAttempt,
+ arg.AttemptIndex,
+ arg.ResolutionTime,
+ arg.ResolutionType,
+ arg.FailureSourceIndex,
+ arg.HtlcFailReason,
+ arg.FailureMsg,
+ )
+ return err
+}
+
const fetchAllInflightAttempts = `-- name: FetchAllInflightAttempts :many
SELECT
ha.id,
@@ -644,3 +684,351 @@ func (q *Queries) FilterPayments(ctx context.Context, arg FilterPaymentsParams)
}
return items, nil
}
+
+const insertHtlcAttempt = `-- name: InsertHtlcAttempt :one
+INSERT INTO payment_htlc_attempts (
+ payment_id,
+ attempt_index,
+ session_key,
+ attempt_time,
+ payment_hash,
+ first_hop_amount_msat,
+ route_total_time_lock,
+ route_total_amount,
+ route_source_key)
+VALUES (
+ $1,
+ $2,
+ $3,
+ $4,
+ $5,
+ $6,
+ $7,
+ $8,
+ $9)
+RETURNING id
+`
+
+type InsertHtlcAttemptParams struct {
+ PaymentID int64
+ AttemptIndex int64
+ SessionKey []byte
+ AttemptTime time.Time
+ PaymentHash []byte
+ FirstHopAmountMsat int64
+ RouteTotalTimeLock int32
+ RouteTotalAmount int64
+ RouteSourceKey []byte
+}
+
+func (q *Queries) InsertHtlcAttempt(ctx context.Context, arg InsertHtlcAttemptParams) (int64, error) {
+ row := q.db.QueryRowContext(ctx, insertHtlcAttempt,
+ arg.PaymentID,
+ arg.AttemptIndex,
+ arg.SessionKey,
+ arg.AttemptTime,
+ arg.PaymentHash,
+ arg.FirstHopAmountMsat,
+ arg.RouteTotalTimeLock,
+ arg.RouteTotalAmount,
+ arg.RouteSourceKey,
+ )
+ var id int64
+ err := row.Scan(&id)
+ return id, err
+}
+
+const insertPayment = `-- name: InsertPayment :one
+INSERT INTO payments (
+ amount_msat,
+ created_at,
+ payment_identifier,
+ fail_reason)
+VALUES (
+ $1,
+ $2,
+ $3,
+ NULL
+)
+RETURNING id
+`
+
+type InsertPaymentParams struct {
+ AmountMsat int64
+ CreatedAt time.Time
+ PaymentIdentifier []byte
+}
+
+// Insert a new payment and return its ID.
+func (q *Queries) InsertPayment(ctx context.Context, arg InsertPaymentParams) (int64, error) {
+ row := q.db.QueryRowContext(ctx, insertPayment, arg.AmountMsat, arg.CreatedAt, arg.PaymentIdentifier)
+ var id int64
+ err := row.Scan(&id)
+ return id, err
+}
+
+const insertPaymentAttemptFirstHopCustomRecord = `-- name: InsertPaymentAttemptFirstHopCustomRecord :exec
+INSERT INTO payment_attempt_first_hop_custom_records (
+ htlc_attempt_index,
+ key,
+ value
+)
+VALUES (
+ $1,
+ $2,
+ $3
+)
+`
+
+type InsertPaymentAttemptFirstHopCustomRecordParams struct {
+ HtlcAttemptIndex int64
+ Key int64
+ Value []byte
+}
+
+func (q *Queries) InsertPaymentAttemptFirstHopCustomRecord(ctx context.Context, arg InsertPaymentAttemptFirstHopCustomRecordParams) error {
+ _, err := q.db.ExecContext(ctx, insertPaymentAttemptFirstHopCustomRecord, arg.HtlcAttemptIndex, arg.Key, arg.Value)
+ return err
+}
+
+const insertPaymentFirstHopCustomRecord = `-- name: InsertPaymentFirstHopCustomRecord :exec
+INSERT INTO payment_first_hop_custom_records (
+ payment_id,
+ key,
+ value
+)
+VALUES (
+ $1,
+ $2,
+ $3
+)
+`
+
+type InsertPaymentFirstHopCustomRecordParams struct {
+ PaymentID int64
+ Key int64
+ Value []byte
+}
+
+func (q *Queries) InsertPaymentFirstHopCustomRecord(ctx context.Context, arg InsertPaymentFirstHopCustomRecordParams) error {
+ _, err := q.db.ExecContext(ctx, insertPaymentFirstHopCustomRecord, arg.PaymentID, arg.Key, arg.Value)
+ return err
+}
+
+const insertPaymentHopCustomRecord = `-- name: InsertPaymentHopCustomRecord :exec
+INSERT INTO payment_hop_custom_records (
+ hop_id,
+ key,
+ value
+)
+VALUES (
+ $1,
+ $2,
+ $3
+)
+`
+
+type InsertPaymentHopCustomRecordParams struct {
+ HopID int64
+ Key int64
+ Value []byte
+}
+
+func (q *Queries) InsertPaymentHopCustomRecord(ctx context.Context, arg InsertPaymentHopCustomRecordParams) error {
+ _, err := q.db.ExecContext(ctx, insertPaymentHopCustomRecord, arg.HopID, arg.Key, arg.Value)
+ return err
+}
+
+const insertPaymentIntent = `-- name: InsertPaymentIntent :one
+INSERT INTO payment_intents (
+ payment_id,
+ intent_type,
+ intent_payload)
+VALUES (
+ $1,
+ $2,
+ $3
+)
+RETURNING id
+`
+
+type InsertPaymentIntentParams struct {
+ PaymentID int64
+ IntentType int16
+ IntentPayload []byte
+}
+
+// Insert a payment intent for a given payment and return its ID.
+func (q *Queries) InsertPaymentIntent(ctx context.Context, arg InsertPaymentIntentParams) (int64, error) {
+ row := q.db.QueryRowContext(ctx, insertPaymentIntent, arg.PaymentID, arg.IntentType, arg.IntentPayload)
+ var id int64
+ err := row.Scan(&id)
+ return id, err
+}
+
+const insertRouteHop = `-- name: InsertRouteHop :one
+INSERT INTO payment_route_hops (
+ htlc_attempt_index,
+ hop_index,
+ pub_key,
+ scid,
+ outgoing_time_lock,
+ amt_to_forward,
+ meta_data
+)
+VALUES (
+ $1,
+ $2,
+ $3,
+ $4,
+ $5,
+ $6,
+ $7
+)
+RETURNING id
+`
+
+type InsertRouteHopParams struct {
+ HtlcAttemptIndex int64
+ HopIndex int32
+ PubKey []byte
+ Scid string
+ OutgoingTimeLock int32
+ AmtToForward int64
+ MetaData []byte
+}
+
+func (q *Queries) InsertRouteHop(ctx context.Context, arg InsertRouteHopParams) (int64, error) {
+ row := q.db.QueryRowContext(ctx, insertRouteHop,
+ arg.HtlcAttemptIndex,
+ arg.HopIndex,
+ arg.PubKey,
+ arg.Scid,
+ arg.OutgoingTimeLock,
+ arg.AmtToForward,
+ arg.MetaData,
+ )
+ var id int64
+ err := row.Scan(&id)
+ return id, err
+}
+
+const insertRouteHopAmp = `-- name: InsertRouteHopAmp :exec
+INSERT INTO payment_route_hop_amp (
+ hop_id,
+ root_share,
+ set_id,
+ child_index
+)
+VALUES (
+ $1,
+ $2,
+ $3,
+ $4
+)
+`
+
+type InsertRouteHopAmpParams struct {
+ HopID int64
+ RootShare []byte
+ SetID []byte
+ ChildIndex int32
+}
+
+func (q *Queries) InsertRouteHopAmp(ctx context.Context, arg InsertRouteHopAmpParams) error {
+ _, err := q.db.ExecContext(ctx, insertRouteHopAmp,
+ arg.HopID,
+ arg.RootShare,
+ arg.SetID,
+ arg.ChildIndex,
+ )
+ return err
+}
+
+const insertRouteHopBlinded = `-- name: InsertRouteHopBlinded :exec
+INSERT INTO payment_route_hop_blinded (
+ hop_id,
+ encrypted_data,
+ blinding_point,
+ blinded_path_total_amt
+)
+VALUES (
+ $1,
+ $2,
+ $3,
+ $4
+)
+`
+
+type InsertRouteHopBlindedParams struct {
+ HopID int64
+ EncryptedData []byte
+ BlindingPoint []byte
+ BlindedPathTotalAmt sql.NullInt64
+}
+
+func (q *Queries) InsertRouteHopBlinded(ctx context.Context, arg InsertRouteHopBlindedParams) error {
+ _, err := q.db.ExecContext(ctx, insertRouteHopBlinded,
+ arg.HopID,
+ arg.EncryptedData,
+ arg.BlindingPoint,
+ arg.BlindedPathTotalAmt,
+ )
+ return err
+}
+
+const insertRouteHopMpp = `-- name: InsertRouteHopMpp :exec
+INSERT INTO payment_route_hop_mpp (
+ hop_id,
+ payment_addr,
+ total_msat
+)
+VALUES (
+ $1,
+ $2,
+ $3
+)
+`
+
+type InsertRouteHopMppParams struct {
+ HopID int64
+ PaymentAddr []byte
+ TotalMsat int64
+}
+
+func (q *Queries) InsertRouteHopMpp(ctx context.Context, arg InsertRouteHopMppParams) error {
+ _, err := q.db.ExecContext(ctx, insertRouteHopMpp, arg.HopID, arg.PaymentAddr, arg.TotalMsat)
+ return err
+}
+
+const settleAttempt = `-- name: SettleAttempt :exec
+INSERT INTO payment_htlc_attempt_resolutions (
+ attempt_index,
+ resolution_time,
+ resolution_type,
+ settle_preimage
+)
+VALUES (
+ $1,
+ $2,
+ $3,
+ $4
+)
+`
+
+type SettleAttemptParams struct {
+ AttemptIndex int64
+ ResolutionTime time.Time
+ ResolutionType int32
+ SettlePreimage []byte
+}
+
+func (q *Queries) SettleAttempt(ctx context.Context, arg SettleAttemptParams) error {
+ _, err := q.db.ExecContext(ctx, settleAttempt,
+ arg.AttemptIndex,
+ arg.ResolutionTime,
+ arg.ResolutionType,
+ arg.SettlePreimage,
+ )
+ return err
+}
diff --git a/sqldb/sqlc/querier.go b/sqldb/sqlc/querier.go
index 9ba6f66..9810825 100644
--- a/sqldb/sqlc/querier.go
+++ b/sqldb/sqlc/querier.go
@@ -34,6 +34,7 @@ type Querier interface {
DeletePruneLogEntriesInRange(ctx context.Context, arg DeletePruneLogEntriesInRangeParams) error
DeleteUnconnectedNodes(ctx context.Context) ([][]byte, error)
DeleteZombieChannel(ctx context.Context, arg DeleteZombieChannelParams) (sql.Result, error)
+ FailAttempt(ctx context.Context, arg FailAttemptParams) error
FetchAMPSubInvoiceHTLCs(ctx context.Context, arg FetchAMPSubInvoiceHTLCsParams) ([]FetchAMPSubInvoiceHTLCsRow, error)
FetchAMPSubInvoices(ctx context.Context, arg FetchAMPSubInvoicesParams) ([]AmpSubInvoice, error)
// Fetch all inflight attempts across all payments
@@ -147,6 +148,7 @@ type Querier interface {
// UpsertEdgePolicy query is used because of the constraint in that query that
// requires a policy update to have a newer last_update than the existing one).
InsertEdgePolicyMig(ctx context.Context, arg InsertEdgePolicyMigParams) (int64, error)
+ InsertHtlcAttempt(ctx context.Context, arg InsertHtlcAttemptParams) (int64, error)
InsertInvoice(ctx context.Context, arg InsertInvoiceParams) (int64, error)
InsertInvoiceFeature(ctx context.Context, arg InsertInvoiceFeatureParams) error
InsertInvoiceHTLC(ctx context.Context, arg InsertInvoiceHTLCParams) (int64, error)
@@ -160,6 +162,17 @@ type Querier interface {
// is used because of the constraint in that query that requires a node update
// to have a newer last_update than the existing node).
InsertNodeMig(ctx context.Context, arg InsertNodeMigParams) (int64, error)
+ // Insert a new payment and return its ID.
+ InsertPayment(ctx context.Context, arg InsertPaymentParams) (int64, error)
+ InsertPaymentAttemptFirstHopCustomRecord(ctx context.Context, arg InsertPaymentAttemptFirstHopCustomRecordParams) error
+ InsertPaymentFirstHopCustomRecord(ctx context.Context, arg InsertPaymentFirstHopCustomRecordParams) error
+ InsertPaymentHopCustomRecord(ctx context.Context, arg InsertPaymentHopCustomRecordParams) error
+ // Insert a payment intent for a given payment and return its ID.
+ InsertPaymentIntent(ctx context.Context, arg InsertPaymentIntentParams) (int64, error)
+ InsertRouteHop(ctx context.Context, arg InsertRouteHopParams) (int64, error)
+ InsertRouteHopAmp(ctx context.Context, arg InsertRouteHopAmpParams) error
+ InsertRouteHopBlinded(ctx context.Context, arg InsertRouteHopBlindedParams) error
+ InsertRouteHopMpp(ctx context.Context, arg InsertRouteHopMppParams) error
IsClosedChannel(ctx context.Context, scid []byte) (bool, error)
IsPublicV1Node(ctx context.Context, pubKey []byte) (bool, error)
IsPublicV2Node(ctx context.Context, pubKey []byte) (bool, error)
@@ -181,6 +194,7 @@ type Querier interface {
OnInvoiceSettled(ctx context.Context, arg OnInvoiceSettledParams) error
SetKVInvoicePaymentHash(ctx context.Context, arg SetKVInvoicePaymentHashParams) error
SetMigration(ctx context.Context, arg SetMigrationParams) error
+ SettleAttempt(ctx context.Context, arg SettleAttemptParams) error
UpdateAMPSubInvoiceHTLCPreimage(ctx context.Context, arg UpdateAMPSubInvoiceHTLCPreimageParams) (sql.Result, error)
UpdateAMPSubInvoiceState(ctx context.Context, arg UpdateAMPSubInvoiceStateParams) error
UpdateInvoiceAmountPaid(ctx context.Context, arg UpdateInvoiceAmountPaidParams) (sql.Result, error)
diff --git a/sqldb/sqlc/queries/payments.sql b/sqldb/sqlc/queries/payments.sql
index b35919c..b0183cc 100644
--- a/sqldb/sqlc/queries/payments.sql
+++ b/sqldb/sqlc/queries/payments.sql
@@ -170,3 +170,185 @@ DELETE FROM payments WHERE id = $1;
DELETE FROM payment_htlc_attempts WHERE payment_id = $1 AND attempt_index IN (
SELECT attempt_index FROM payment_htlc_attempt_resolutions WHERE resolution_type = 2
);
+
+-- name: InsertPaymentIntent :one
+-- Insert a payment intent for a given payment and return its ID.
+INSERT INTO payment_intents (
+ payment_id,
+ intent_type,
+ intent_payload)
+VALUES (
+ @payment_id,
+ @intent_type,
+ @intent_payload
+)
+RETURNING id;
+
+-- name: InsertPayment :one
+-- Insert a new payment and return its ID.
+-- When creating a payment we don't have a fail reason because we start the
+-- payment process.
+INSERT INTO payments (
+ amount_msat,
+ created_at,
+ payment_identifier,
+ fail_reason)
+VALUES (
+ @amount_msat,
+ @created_at,
+ @payment_identifier,
+ NULL
+)
+RETURNING id;
+
+-- name: InsertPaymentFirstHopCustomRecord :exec
+INSERT INTO payment_first_hop_custom_records (
+ payment_id,
+ key,
+ value
+)
+VALUES (
+ @payment_id,
+ @key,
+ @value
+);
+
+-- name: InsertHtlcAttempt :one
+INSERT INTO payment_htlc_attempts (
+ payment_id,
+ attempt_index,
+ session_key,
+ attempt_time,
+ payment_hash,
+ first_hop_amount_msat,
+ route_total_time_lock,
+ route_total_amount,
+ route_source_key)
+VALUES (
+ @payment_id,
+ @attempt_index,
+ @session_key,
+ @attempt_time,
+ @payment_hash,
+ @first_hop_amount_msat,
+ @route_total_time_lock,
+ @route_total_amount,
+ @route_source_key)
+RETURNING id;
+
+-- name: InsertPaymentAttemptFirstHopCustomRecord :exec
+INSERT INTO payment_attempt_first_hop_custom_records (
+ htlc_attempt_index,
+ key,
+ value
+)
+VALUES (
+ @htlc_attempt_index,
+ @key,
+ @value
+);
+
+-- name: InsertRouteHop :one
+INSERT INTO payment_route_hops (
+ htlc_attempt_index,
+ hop_index,
+ pub_key,
+ scid,
+ outgoing_time_lock,
+ amt_to_forward,
+ meta_data
+)
+VALUES (
+ @htlc_attempt_index,
+ @hop_index,
+ @pub_key,
+ @scid,
+ @outgoing_time_lock,
+ @amt_to_forward,
+ @meta_data
+)
+RETURNING id;
+
+-- name: InsertRouteHopMpp :exec
+INSERT INTO payment_route_hop_mpp (
+ hop_id,
+ payment_addr,
+ total_msat
+)
+VALUES (
+ @hop_id,
+ @payment_addr,
+ @total_msat
+);
+
+-- name: InsertRouteHopAmp :exec
+INSERT INTO payment_route_hop_amp (
+ hop_id,
+ root_share,
+ set_id,
+ child_index
+)
+VALUES (
+ @hop_id,
+ @root_share,
+ @set_id,
+ @child_index
+);
+
+-- name: InsertRouteHopBlinded :exec
+INSERT INTO payment_route_hop_blinded (
+ hop_id,
+ encrypted_data,
+ blinding_point,
+ blinded_path_total_amt
+)
+VALUES (
+ @hop_id,
+ @encrypted_data,
+ @blinding_point,
+ @blinded_path_total_amt
+);
+
+-- name: InsertPaymentHopCustomRecord :exec
+INSERT INTO payment_hop_custom_records (
+ hop_id,
+ key,
+ value
+)
+VALUES (
+ @hop_id,
+ @key,
+ @value
+);
+
+-- name: SettleAttempt :exec
+INSERT INTO payment_htlc_attempt_resolutions (
+ attempt_index,
+ resolution_time,
+ resolution_type,
+ settle_preimage
+)
+VALUES (
+ @attempt_index,
+ @resolution_time,
+ @resolution_type,
+ @settle_preimage
+);
+
+-- name: FailAttempt :exec
+INSERT INTO payment_htlc_attempt_resolutions (
+ attempt_index,
+ resolution_time,
+ resolution_type,
+ failure_source_index,
+ htlc_fail_reason,
+ failure_msg
+)
+VALUES (
+ @attempt_index,
+ @resolution_time,
+ @resolution_type,
+ @failure_source_index,
+ @htlc_fail_reason,
+ @failure_msg
+);
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.