lnd icon indicating copy to clipboard operation
lnd copied to clipboard

[Part 1|3] Introduce SQL Payment schema into LND

Open ziggie1984 opened this issue 1 year ago • 12 comments

Created a draft of the schema, will now proceed to create the whole sqlc harness now. If you have already feedback feel free to reach out.

Payment SQL Schema

Needs the refactor series as a prerequestie:

Depends on:

  • #9822
  • #9825
  • #9826

ziggie1984 avatar Oct 01 '24 13:10 ziggie1984

[!IMPORTANT]

Review skipped

Auto reviews are limited to specific labels.

:label: Labels to auto review (1)
  • llm-review

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

coderabbitai[bot] avatar Oct 01 '24 13:10 coderabbitai[bot]

Uploading Payment Schema.png…

ziggie1984 avatar Oct 01 '24 13:10 ziggie1984

Some questions I already had:

  1. I connected most of the tables via the payment id (primary key) however does it make sense to also include a foreign key which points to the payment hash or even have 2 foreign keys for tables (or is the lookup cost very low so we can go with 1 and from the payment id just get the payment hash from the mpp_payment table.
  2. I removed the need to have a separate table for PaymentCreationInfo but put everything into mpp_payment record this will probably force use to also change the interface of the control_tower for example:
	InitPayment(lntypes.Hash, *channeldb.PaymentCreationInfo) error

I think this is ok and we just include an interface for the channeldb.PaymentCreationInfo type.

ziggie1984 avatar Oct 01 '24 14:10 ziggie1984

) however does it make sense to also include a foreign key which points to the payment hash or even have 2 foreign keys for tables (or is the lookup cost very low so we can go with 1 and from the payment id just get the payment hash from the mpp_payment table.

As in a composite foreign key? That should be fine, though if there's a way we can avoid it in practice to simplify queries or instead add a UNIQUE index, then that might be desirable.

Roasbeef avatar Oct 07 '24 15:10 Roasbeef

@ziggie1984 I think the image upload on that last comment failed.

Roasbeef avatar Oct 07 '24 15:10 Roasbeef

I used Claude to create an mermaid diagram of the schema so far (zero shot, and first pass, might have some errors). This is nice, as we can check it into the repo, and the markdown updates will be shown in the git diff:

erDiagram
    mpp_payment ||--o{ first_hop_custom_records : has
    mpp_payment ||--|| payment_status_types : has
    mpp_payment ||--|| mpp_state : has
    mpp_payment ||--o{ htlc_attempt : has
    htlc_attempt ||--o| htlc_settle_info : has
    htlc_attempt ||--o| htlc_fail_info : has
    htlc_fail_info ||--|| htlc_fail_reason_types : has
    htlc_fail_info ||--o| failure_msg_types : has
    htlc_attempt ||--o| route : has
    route ||--o{ hop : contains
    hop ||--o| blinded_data : has
    hop ||--o| mpp_record : has
    hop ||--o| amp_record : has
    hop ||--o{ hop_custom_records : has

    mpp_payment {
        integer id PK
        integer payment_status FK
        blob payment_hash
        bigint amount_msat
        blob payment_request
        timestamp created_at
    }
    first_hop_custom_records {
        bigint key FK
        blob value
        text payment_id
    }
    payment_status_types {
        integer id PK
        text description
    }
    mpp_state {
        integer payment_id FK
        integer num_attempts_in_flight
        bigint remaining_amt
        bigint FeesPaid
        boolean has_settled_htlc
        boolean payment_failed
    }
    htlc_attempt {
        integer id PK,FK
        blob session_key
        timestamp attempt_time
        integer payment_id FK
    }
    htlc_settle_info {
        integer id PK
        integer attempt_id FK
        blob preimage
        timestamp settle_time
    }
    htlc_fail_info {
        integer id PK
        integer attempt_id FK
        integer failure_source_index
        integer htlc_fail_reason FK
        integer failure_msg FK
    }
    htlc_fail_reason_types {
        integer id PK
        text description
    }
    failure_msg_types {
        integer id PK
        text description
        text error_msg
    }
    route {
        integer id PK
        integer htlc_attempt_id FK
        integer total_timeLock
        bigint total_amount
        blob source_key
        bigint first_hop_amount
    }
    hop {
        integer id PK
        integer route_id FK
        blob pub_key
        text chan_id
        integer outgoing_time_lock
        bigint amt_to_forward
        blob meta_data
    }
    blinded_data {
        integer hop_id FK
        blob encrypted_data
        blob blinding_point
        bigint total_amt
    }
    mpp_record {
        integer hop_id FK
        blob payment_addr
        bigint total_msat
    }
    amp_record {
        integer hop_id FK
        blob root_share
        blob set_id
        integer child_index
    }
    hop_custom_records {
        bigint key FK
        blob value
        integer hop_id FK
    }

Roasbeef avatar Oct 07 '24 15:10 Roasbeef

Updated version:

erDiagram
    payments {
        BIGINT id PK
        INTEGER payment_status FK
        INTEGER payment_type FK
        BIGINT amount_msat
        BLOB payment_request
        TIMESTAMP created_at
    }
    payment_status_types {
        INTEGER id PK
        TEXT description
    }
    payment_types {
        INTEGER id PK
        TEXT description
    }
    legacy_payments {
        INTEGER payment_id PK,FK
        BLOB payment_hash
    }
    mpp_payments {
        INTEGER payment_id PK,FK
        INTEGER mpp_record_id FK
    }
    amp_payments {
        INTEGER payment_id PK,FK
        INTEGER amp_record_id FK
    }
    mpp_state {
        INTEGER payment_id PK,FK
        INTEGER num_attempts_in_flight
        BIGINT remaining_amt
        BIGINT FeesPaid
        BOOLEAN has_settled_htlc
        BOOLEAN payment_failed
    }
    first_hop_custom_records {
        BIGINT key
        BLOB value
        INTEGER payment_id FK
    }
    htlc_attempt {
        INTEGER id PK
        BLOB session_key
        TIMESTAMP attempt_time
        INTEGER payment_id FK
    }
    route {
        INTEGER htlc_attempt_id PK,FK
        INTEGER total_timeLock
        BIGINT total_amount
        BLOB source_key
        BIGINT first_hop_amount
    }
    hop {
        INTEGER id PK
        INTEGER route_id FK
        BLOB pub_key
        TEXT chan_id
        INTEGER outgoing_time_lock
        BIGINT amt_to_forward
        BLOB meta_data
    }
    mpp_record {
        INTEGER hop_id PK,FK
        BLOB payment_addr
        BIGINT total_msat
    }
    amp_record {
        INTEGER hop_id PK,FK
        BLOB root_share
        BLOB set_id
        INTEGER child_index
    }
    hop_custom_records {
        BIGINT key
        BLOB value
        BIGINT hop_id FK
    }
    blinded_data {
        INTEGER hop_id PK,FK
        BLOB encrypted_data
        BLOB blinding_point
        BIGINT total_amt
    }
    htlc_settle_info {
        INTEGER htlc_attempt_id PK,FK
        BLOB preimage
        TIMESTAMP settle_time
    }
    htlc_fail_info {
        INTEGER htlc_attempt_id PK,FK
        INTEGER htlc_fail_reason FK
        TEXT failure_msg
    }
    htlc_fail_reason_types {
        INTEGER id PK
        TEXT description
    }

    payments ||--o{ legacy_payments : has
    payments ||--o{ mpp_payments : has
    payments ||--o{ amp_payments : has
    payments ||--o{ mpp_state : has
    payments ||--o{ first_hop_custom_records : has
    payments ||--o{ htlc_attempt : has
    payments }|--|| payment_status_types : references
    payments }|--|| payment_types : references
    mpp_payments }|--|| mpp_record : references
    amp_payments }|--|| amp_record : references
    htlc_attempt ||--o| route : has
    route ||--o{ hop : contains
    hop ||--o| mpp_record : has
    hop ||--o| amp_record : has
    hop ||--o{ hop_custom_records : has
    hop ||--o| blinded_data : has
    htlc_attempt ||--o| htlc_settle_info : has
    htlc_attempt ||--o| htlc_fail_info : has
    htlc_fail_info }|--|| htlc_fail_reason_types : references

Roasbeef avatar Oct 07 '24 16:10 Roasbeef

Updated graph:

erDiagram
    payments {
        BIGINT id PK
        INTEGER payment_status FK
        INTEGER payment_type FK
        BIGINT amount_msat
        TIMESTAMP created_at
    }
    payment_requests {
        INTEGER id PK
        INTEGER payment_id FK
        BLOB payment_request
    }
    payment_status_types {
        INTEGER id PK
        TEXT description
    }
    payment_types {
        INTEGER id PK
        TEXT description
    }
    legacy_payments {
        INTEGER payment_id PK,FK
        BLOB payment_hash
    }
    mpp_payments {
        INTEGER payment_id PK,FK
        INTEGER mpp_record_id FK
    }
    amp_payments {
        INTEGER payment_id PK,FK
        INTEGER amp_record_id FK
    }
    payment_state {
        INTEGER payment_id PK,FK
        INTEGER num_attempts_in_flight
        BIGINT remaining_amt
        BIGINT fees_paid
        BOOLEAN has_settled_htlc
        BOOLEAN payment_failed
    }
    htlc_attempt {
        INTEGER id PK
        BLOB session_key
        TIMESTAMP attempt_time
        INTEGER payment_id FK
    }
    route {
        INTEGER htlc_attempt_id PK,FK
        INTEGER total_timeLock
        BIGINT total_amount
        BLOB source_key
        BIGINT first_hop_amount
    }
    hop {
        INTEGER id PK
        INTEGER route_id FK
        BLOB pub_key
        TEXT chan_id
        INTEGER outgoing_time_lock
        BIGINT amt_to_forward
        BLOB meta_data
    }
    mpp_record {
        INTEGER hop_id PK,FK
        BLOB payment_addr
        BIGINT total_msat
    }
    amp_record {
        INTEGER hop_id PK,FK
        BLOB root_share
        BLOB set_id
        INTEGER child_index
    }
    tlv_records {
        INTEGER id PK
        BIGINT key
        BLOB value
    }
    custom_records {
        INTEGER tlv_record_id PK,FK
        INTEGER hop_id FK
    }
    first_hop_custom_records {
        INTEGER tlv_record_id PK,FK
        INTEGER payment_id FK
    }
    blinded_data {
        INTEGER hop_id PK,FK
        BLOB encrypted_data
        BLOB blinding_point
        BIGINT total_amt
    }
    htlc_settle_info {
        INTEGER htlc_attempt_id PK,FK
        BLOB preimage
        TIMESTAMP settle_time
    }
    htlc_fail_info {
        INTEGER htlc_attempt_id PK,FK
        INTEGER htlc_fail_reason FK
        TEXT failure_msg
    }
    htlc_fail_reason_types {
        INTEGER id PK
        TEXT description
    }

    payments ||--o{ payment_requests : has
    payments ||--o| legacy_payments : has
    payments ||--o| mpp_payments : has
    payments ||--o| amp_payments : has
    payments ||--o| payment_state : has
    payments ||--o{ htlc_attempt : has
    payments }o--|| payment_status_types : has
    payments }o--|| payment_types : has
    htlc_attempt ||--o| route : has
    route ||--o{ hop : has
    hop ||--o| mpp_record : has
    hop ||--o| amp_record : has
    hop ||--o{ custom_records : has
    hop ||--o| blinded_data : has
    htlc_attempt ||--o| htlc_settle_info : has
    htlc_attempt ||--o| htlc_fail_info : has
    htlc_fail_info }o--|| htlc_fail_reason_types : has
    mpp_record ||--|| mpp_payments : references
    amp_record ||--|| amp_payments : references
    tlv_records ||--o{ custom_records : has
    tlv_records ||--o{ first_hop_custom_records : has
    payments ||--o{ first_hop_custom_records : has
    

ziggie1984 avatar Oct 08 '24 08:10 ziggie1984

Currently creating the whole change into the one draft PR and then if it all works together will split all the different parts into separate PRs.

ziggie1984 avatar Oct 25 '24 13:10 ziggie1984

List of ideas to include during refactor (in progress):

  • garbage collector for failed payments on startup

ziggie1984 avatar Oct 25 '24 14:10 ziggie1984

fix the nil hash patch properly via SQL: https://github.com/lightningnetwork/lnd/pull/9703#issuecomment-2801670538

ziggie1984 avatar Apr 14 '25 14:04 ziggie1984

New updated Diagram: https://dbdiagram.io/d/Payment-Schema-66fbb44df9b1444815012417

ziggie1984 avatar May 20 '25 15:05 ziggie1984

DB design changed quite a bit:

  erDiagram
      payments {
          integer id PK
          blob payment_request
          bigint amount_msat
          timestamp created_at
          blob payment_hash UK "UNIQUE"
          integer fail_reason
      }

      payment_htlc_attempts {
          integer id PK
          bigint attempt_index UK "UNIQUE"
          bigint payment_id FK
          blob session_key UK "UNIQUE"
          timestamp attempt_time
          blob payment_hash
          bigint first_hop_amount_msat
          integer route_total_time_lock
          bigint route_total_amount
          blob route_source_key
          integer failure_source_index
          integer htlc_fail_reason
          blob failure_msg
          timestamp fail_time
          blob settle_preimage
          timestamp settle_time
      }

      payment_route_hops {
          integer id PK
          bigint htlc_attempt_index FK
          integer hop_index
          blob pub_key
          text scid
          integer outgoing_time_lock
          bigint amt_to_forward
          blob meta_data
          boolean legacy_payload
          blob mpp_payment_addr
          bigint mpp_total_msat
          blob amp_root_share
          blob amp_set_id
          integer amp_child_index
          blob encrypted_data
          blob blinding_point
          bigint blinded_path_total_amt
      }

      payment_first_hop_custom_records {
          integer id PK
          bigint key
          blob value
          bigint payment_id FK
      }

      payment_htlc_attempt_custom_records {
          integer id PK
          bigint key
          blob value
          bigint htlc_attempt_index FK
      }

      payment_route_hop_custom_records {
          integer id PK
          bigint key
          blob value
          bigint hop_id FK
      }

      %% Relationships
      payments ||--o{ payment_htlc_attempts : "has many"
      payments ||--o{ payment_first_hop_custom_records : "has many"

      payment_htlc_attempts ||--o{ payment_route_hops : "has many"
      payment_htlc_attempts ||--o{ payment_htlc_attempt_custom_records : "has many"

      payment_route_hops ||--o{ payment_route_hop_custom_records : "has many"

ziggie1984 avatar Aug 26 '25 17:08 ziggie1984

Probably we can move all the amp,mpp,blinded data fields into a separate table and link it from the hop table, because for example most of the stuff is only part of the last hop (for AMP,MPP) so we avoid carrying all this NULL fields in every hop for every payment ?

ziggie1984 avatar Aug 27 '25 11:08 ziggie1984

/gemini-code-assist review

ziggie1984 avatar Aug 27 '25 14:08 ziggie1984

Acknowledged. Initiating a review of the pull request. Please allow some time for the analysis to complete.

gemini-code-assist[bot] avatar Aug 27 '25 14:08 gemini-code-assist[bot]

/gemini review

ziggie1984 avatar Aug 28 '25 10:08 ziggie1984

can be rebased

yyforyongyu avatar Sep 10 '25 10:09 yyforyongyu

I created a refined schema which is more optimzed:

https://dbdiagram.io/d/Payment-Schema-66fbb44df9b1444815012417

ziggie1984 avatar Sep 29 '25 16:09 ziggie1984

Screenshot 2025-09-29 at 18 22 09

ziggie1984 avatar Sep 29 '25 16:09 ziggie1984

OK so I broke the whole PR into pieces and will use a side branch instead to merge those PRs until the whole feature is ready. The tests which use the nativesql-experiment will fail currently because the backend is not implemented yet.

Couple of words regarding the new schema and the design choices:

Differences to the old design:

  • Removed the Legacy_Payload bit because it is not necessary, will only use the tlv payload and if we still have some old payload which uses the legacy payload format we will deserialize it and move it into the proper table, so there is no need flagging legacy payload.

  • Moved the payment request into its own table to account for Bolt12 offers which will have a offer instead of and invoice we would initiate the payment to.

  • Also decouple the resolution from the attempt table to have them properly separated. I decided against splitting the settle and fail information into its own tables because then we would not have an easy way to check for consitency meaning that either the failure reason for this attempt or the settle info is set.

  • Now a short word to the CustomHop data, we have custom hop data on 3 levels: the payment level (which carries the custom tlv data for the first hop), the attempt/route level tlv custom payload (which is very similar to the payment level tlv custom record payload, but for the custom channels we add additional information via the AuxUnit so we have to persist it as well) and the hop level custom record payload which has the tlv custom record data for all other hops except the first hop. I dedided to keep them separate for now, because having a centralized TLV table and only link tables from the payment,route,hop might save us some data but comes with more complexity.

  • We separated the MPP, AMP and BLinded information into its own tables, because for the MPP and AMP case it only effects the last hop so we would have a lot of waste stored if we integrated it into the. hops table. For the blinded data I also separated it out, because most payment will not have it. and a small join will not effect much the speed in fetching the blinded data.

  • Also what is important to note: The extra attemptIndex in the attempts table will be removed later down the road before the feature lands into master, as soon as the circuit map dependency is removed, this will happen after the basic SQL functionality is merged into the side branch. It does not hurt much currently and it is a bit of a refactor to remove the dependency to the switch to that we can just use the primary key of the attempt.

ziggie1984 avatar Sep 29 '25 17:09 ziggie1984

@yyforyongyu: review reminder @ziggie1984, remember to re-request review from reviewers when ready

lightninglabs-deploy avatar Oct 08 '25 15:10 lightninglabs-deploy

2025-10-13 07:07:15.700 [DBG] CRTR router.go:1419: Scanning for inflight payments panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x48 pc=0x1bed372]

yes that's expected, we embed the kvstore for the sqlstore for now so that we can compile it, but for the sql tests they will fail because I never initialize this value because it would not work anyways if we partly implement each function because then parts will live in the kvstore and parts in the sql store, thats why for now these itests will fail for the test_native_sql run.

ziggie1984 avatar Oct 13 '25 11:10 ziggie1984