Skip to content

Conversation

@ziggie1984
Copy link
Collaborator

@ziggie1984 ziggie1984 commented Jan 11, 2026

Builds on top of: #10485

This PR is just for completion in case users have duplcate payments and want to access them after the migration.

Before the duplicate payments would be always fetched as well, but since this is a very old bug and most of the nodes should not have any of those duplicate payments I decided to pack them into a different lean table. This PR makes sure people can still access them if they need to.

Add a migration specific query which allows to set the failure
reason when inserting a payment into the db.
Older LND versions could create multiple payments for the same hash.
We need to preserve those historical records during KV→SQL migration,
but they don’t fit the normal payment schema because we enforce a
unique payment hash constraint. Introduce a lean payment_duplicates
table to store only the essential fields (identifier, amount,
timestamps, settle/fail data).

This keeps the primary payment records stable and makes the migration
deterministic even when duplicate records lack attempt info. The table
is intentionally minimal and can be dropped after migration if no
duplicate payments exist.

For now there is no logic in place which allows the noderunner to
fetch duplicate payments after the migration.
Copy the core payments/db helpers into payments/db/migration1 and add the required sqlc-generated types/queries from sqldb/sqlc. This effectively freezes the migration code so it stays robust against future query or schema changes in the main payments package.
Implement the KV→SQL payment migration and add an in-migration validation pass that deep-compares KV and SQL payment data in batches. Duplicate payments are migrated into the payment_duplicates table, and duplicates without attempt info or explicit resolution are marked failed to ensure terminal state. Validation checks those rows as well.
Add test helpers plus sql_migration_test coverage for KV→SQL migration: basic migration, sequence ordering, data integrity, and feature-specific cases (MPP/AMP, custom records, blinded routes, metadata, failure messages). Also cover duplicate payment migration to payment_duplicates, including missing attempt info to ensure terminal failure is recorded.

This gives broad regression coverage for the migration path and its edge-cases.
Add a developer-facing migration_external_test that allows running the KV→SQL payments migration against a real channel.db/channel.sqlite (or a KV postgres backend) to debug migration failures on actual data. The accompanying testdata README documents how to supply a database file and configure the test, so users can validate their data and confirm the migration completes successfully.

The test is skipped by default and meant for manual diagnostics.
Hook the payments KV→SQL migration into the SQL migration config and config builder, add logging for the migration path, and introduce tombstone protection to prevent re-running once migration completes.
Add SQL queries and store APIs to fetch duplicate payments by hash or across all records.

This introduces the duplicate payment domain type, conversion helpers, and the SQL-backed fetch methods, and wires them into the SQL query interface/SQLC bindings.
Add SQL-only tests for fetching duplicate payments, covering per-payment lookup and pagination across multiple payments.
Introduce RPCs for listing duplicate payments and wire them into the main server.

This adds the new proto messages, REST bindings, and rpcserver handlers, and reuses a shared failure-reason marshal helper for consistent enum mapping.
Add hidden lncli commands to call the duplicate payment list RPCs for debugging and inspection.
@gemini-code-assist
Copy link

Summary of Changes

Hello @ziggie1984, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the payment system by exposing legacy duplicate payment records through new gRPC endpoints and lncli commands, exclusively for the SQL backend. A major component of this change is the introduction of a robust migration utility that seamlessly transitions existing payment data from the KV store to the native SQL schema, ensuring data integrity and providing a clear path forward for database evolution.

Highlights

  • Duplicate Payments RPCs: Introduced new gRPC methods (ListPaymentDuplicates and ListAllPaymentDuplicates) and corresponding lncli commands to query legacy duplicate payment records from the SQL backend.
  • KV-to-SQL Payment Migration: Implemented a comprehensive migration process to transfer existing KV-store payment data, including historical duplicate payments, to the new native SQL payments backend. This migration includes robust validation and logging.
  • Payments Database Interface: Added a DuplicatePaymentsReader interface to the paymentsdb package, allowing SQL-backed payment stores to expose duplicate payment fetching capabilities.
  • KV Payments Tombstone: Introduced a 'tombstone' mechanism for the KV payments bucket. After a successful migration to SQL, this tombstone prevents the KV store from being reopened, ensuring data consistency and guiding users to the new SQL backend.
  • Release Notes and Build Updates: Updated release notes to reflect the new KV-to-SQL payment migration and adjusted .gitignore to exclude SQLite database files, along with a go.mod dependency update.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces GPRC methods for handling duplicate payments within the SQL backend, a significant step in the migration from a KV store. The changes are extensive, encompassing new RPCs, CLI commands, database schema updates, and a dedicated migration package. The implementation appears robust, with thorough testing and a clear focus on data integrity. My feedback primarily addresses opportunities to enhance code clarity and ensure full adherence to the repository's style guide, specifically regarding function documentation and error message accuracy.

Action: actionDecorator(listPaymentDuplicates),
}

func listPaymentDuplicates(ctx *cli.Context) error {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This function is missing a comment explaining its purpose. According to the style guide, every function must be commented.

// listPaymentDuplicates is the action for the `listpaymentduplicates` command.
// It retrieves and displays duplicate payment records for a given payment hash.
func listPaymentDuplicates(ctx *cli.Context) error {
References
  1. Every function must be commented with its purpose and assumptions. Function comments must begin with the function name and should be complete sentences. (link)

Action: actionDecorator(listAllPaymentDuplicates),
}

func listAllPaymentDuplicates(ctx *cli.Context) error {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This function is missing a comment explaining its purpose. According to the style guide, every function must be commented.

// listAllPaymentDuplicates is the action for the `listallpaymentduplicates`
// command. It retrieves and displays all duplicate payment records across all
// payments.
func listAllPaymentDuplicates(ctx *cli.Context) error {
References
  1. Every function must be commented with its purpose and assumptions. Function comments must begin with the function name and should be complete sentences. (link)

Comment on lines +28 to +29
return fmt.Errorf("payments bucket does not "+
"exist: %w", err)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error message "payments bucket does not exist" is confusing here, as it's returned from a code block that attempts to create the bucket if it doesn't exist. A more accurate message would clarify that the creation failed.

Suggested change
return fmt.Errorf("payments bucket does not "+
"exist: %w", err)
return fmt.Errorf("failed to create payments "+
"bucket: %w", err)

return c, nil
}

func fetchDuplicatePayment(bucket kvdb.RBucket) (*MPPayment, error) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This function is missing a comment explaining its purpose. According to the style guide, every function must be commented.

// fetchDuplicatePayment deserializes a legacy duplicate payment from the
// given bucket.
func fetchDuplicatePayment(bucket kvdb.RBucket) (*MPPayment, error) {
References
  1. Every function must be commented with its purpose and assumptions. Function comments must begin with the function name and should be complete sentences. (link)

return payment, nil
}

func fetchDuplicatePayments(paymentHashBucket kvdb.RBucket) ([]*MPPayment,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This function is missing a comment explaining its purpose. According to the style guide, every function must be commented.

// fetchDuplicatePayments fetches all legacy duplicate payments for a given
// payment hash bucket.
func fetchDuplicatePayments(paymentHashBucket kvdb.RBucket) ([]*MPPayment,
References
  1. Every function must be commented with its purpose and assumptions. Function comments must begin with the function name and should be complete sentences. (link)

Comment on lines +232 to +233
return fmt.Errorf("non bucket element" +
"in duplicate bucket")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's a missing space in the error string concatenation, which will result in the message "non bucket elementin duplicate bucket".

return fmt.Errorf("non bucket element " +
	"in duplicate bucket")


// parseDuplicateSettleData extracts settle data from either legacy or modern
// duplicate formats.
func parseDuplicateSettleData(settleData []byte) ([]byte, sql.NullTime, error) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This function is missing a comment explaining its purpose. According to the style guide, every function must be commented.

Suggested change
func parseDuplicateSettleData(settleData []byte) ([]byte, sql.NullTime, error) {
// parseDuplicateSettleData extracts settle data from either legacy or modern
// duplicate formats. Legacy formats only contain the preimage, while modern
// formats contain a serialized HTLCSettleInfo.
func parseDuplicateSettleData(settleData []byte) ([]byte, sql.NullTime, error) {
References
  1. Every function must be commented with its purpose and assumptions. Function comments must begin with the function name and should be complete sentences. (link)

return resp, nil
}

func paymentDuplicateToRPC(dup *paymentsdb.DuplicatePayment) (

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This function is missing a comment explaining its purpose. According to the style guide, every function must be commented.

// paymentDuplicateToRPC converts a paymentsdb.DuplicatePayment to its
// corresponding lnrpc.PaymentDuplicate representation.
func paymentDuplicateToRPC(dup *paymentsdb.DuplicatePayment) (
References
  1. Every function must be commented with its purpose and assumptions. Function comments must begin with the function name and should be complete sentences. (link)

@ziggie1984 ziggie1984 self-assigned this Jan 11, 2026
@ziggie1984 ziggie1984 added migration payments Related to invoices/payments sql labels Jan 11, 2026
@ziggie1984 ziggie1984 added this to v0.21 Jan 11, 2026
@ziggie1984 ziggie1984 added this to the v0.21.0 milestone Jan 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

migration payments Related to invoices/payments sql

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant