Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

ziggie1984
Copy link
Collaborator

@ziggie1984 ziggie1984 commented Oct 1, 2024

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

Copy link
Contributor

coderabbitai bot commented Oct 1, 2024

Important

Review skipped

Auto reviews are limited to specific labels.

🏷️ 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.

@ziggie1984
Copy link
Collaborator Author

Uploading Payment Schema.png…

@ziggie1984
Copy link
Collaborator Author

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.

@saubyk saubyk added sql payments Related to invoices/payments database Related to the database/storage of LND backend Related to the node backend software/interface (e.g. btcd, bitcoin-core) labels Oct 3, 2024
@saubyk saubyk added this to the v0.19.0 milestone Oct 3, 2024
@Roasbeef
Copy link
Member

Roasbeef commented Oct 7, 2024

) 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
Copy link
Member

Roasbeef commented Oct 7, 2024

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

@Roasbeef
Copy link
Member

Roasbeef commented Oct 7, 2024

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
    }
Loading

amount_msat BIGINT NOT NULL,

-- payment_request is the payment request of the payment.
payment_request BLOB UNIQUE NOT NULL,
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we should split this out, as in the future we'll have more than one type of payment request encoding. I think it's also possible to actually have duplicates here: someone pays a zero value invoice multiple times.

-- invoice_event_types defines the different types of invoice events.
INSERT INTO payment_status_types (id, description)
VALUES
-- status_unknown is a deprecated status and describes the unkown payment
Copy link
Member

Choose a reason for hiding this comment

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

unkown -> unknown



-- invoice_event_types defines the different types of invoice events.
INSERT INTO payment_status_types (id, description)
Copy link
Member

Choose a reason for hiding this comment

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

Do these status values capture the latest fixes/wisdom w.r.t edge cases we've seen in the wild with payments in "stuck" states?

CREATE TABLE legacy_payments (
payment_id INTEGER PRIMARY KEY REFERENCES payments(id) ON DELETE CASCADE,

payment_hash BLOB UNIQUE NOT NULL
Copy link
Member

Choose a reason for hiding this comment

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

I think there were instances of duplicate payment hashes in the past?


remaining_amt BIGINT NOT NULL,

FeesPaid BIGINT NOT NULL,
Copy link
Member

Choose a reason for hiding this comment

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

fees_paid


has_settled_htlc BOOLEAN NOT NULL,

payment_failed BOOLEAN NOT NULL
Copy link
Member

Choose a reason for hiding this comment

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

Do these actually need to be NOT NULL, iiuc that'll just force a caller to always specify something. Might have some implications at the sqlc API level as well.

-- first_hop_custom_records table stores the first hop custom records which are
-- only send as a wire message to the first hop and are used for overlay
-- channels.
CREATE TABLE "first_hop_custom_records" (
Copy link
Member

Choose a reason for hiding this comment

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

WIP, but can drop the quotes from each of the tables below.


value BLOB NOT NULL,

payment_id INTEGER REFERENCES payments(id) ON DELETE CASCADE
Copy link
Member

Choose a reason for hiding this comment

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

Seems we could further denormalize this with another layer of indirection. So we store a table of TLV KV pairs, then an associative table that relates a payment_id to an entry in the TLV KV table.

CREATE INDEX IF NOT EXISTS amp_record_set_id_idx ON amp_record(set_id);

CREATE TABLE "hop_custom_records" (
key BIGINT NOT NULL,
Copy link
Member

Choose a reason for hiding this comment

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

Yeah so if we go with the suggestion above, then this would become another associative table (dedup the KV pairs, then one table to map a given hop_id to the set of KV pairs).

@Roasbeef
Copy link
Member

Roasbeef commented Oct 7, 2024

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
Loading

@ziggie1984
Copy link
Collaborator Author

ziggie1984 commented Oct 8, 2024

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
    
Loading

@ziggie1984 ziggie1984 force-pushed the introduce-sql-schema-payments branch from 9741e37 to 616b851 Compare October 8, 2024 08:49
@ziggie1984 ziggie1984 force-pushed the introduce-sql-schema-payments branch from 616b851 to 9817290 Compare October 8, 2024 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend Related to the node backend software/interface (e.g. btcd, bitcoin-core) database Related to the database/storage of LND payments Related to invoices/payments sql
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

3 participants