Skip to content

Update ldk-node dependency & expose bolt12 proofs - #258

Merged
benthecarman merged 9 commits into
lightningdevkit:mainfrom
benthecarman:update-ldk-node
Sep 8, 2026
Merged

Update ldk-node dependency & expose bolt12 proofs#258
benthecarman merged 9 commits into
lightningdevkit:mainfrom
benthecarman:update-ldk-node

Conversation

@benthecarman

@benthecarman benthecarman commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Update ldk-node and adapt payment events to its current API. Expose payment IDs in events and retain manual handling for unknown BOLT 11 payments.

Add BOLT 12 payer-proof creation to the gRPC, CLI, and MCP interfaces. Include the preimage and invoice in successful-payment events for stateless proof creation.

Use ldk-node pagination for payment history. Remove duplicate payment records from the ldk-server SQLite store, which now contains only forwarded-payment history.

@ldk-reviews-bot

ldk-reviews-bot commented Aug 18, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@benthecarman
benthecarman marked this pull request as ready for review August 18, 2026 05:28
Comment thread ldk-server/src/api/mod.rs Outdated
@wpaulino
wpaulino removed their request for review August 18, 2026 17:19
@benthecarman
benthecarman requested a review from tnull August 19, 2026 22:36
@benthecarman

Copy link
Copy Markdown
Collaborator Author

Rebased and updated ldk-node to new commit with the pagination changes. Now using the paginated payments instead of the ldk-server version

@tnull tnull left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good, but we should probably also include the new channel type in list_channels now.

Comment thread ldk-server/src/api/error.rs
@benthecarman
benthecarman requested a review from tnull August 20, 2026 20:18
@benthecarman
benthecarman force-pushed the update-ldk-node branch 2 times, most recently from 9346952 to 6076c50 Compare August 26, 2026 18:39
@benthecarman
benthecarman requested a review from tankyleo August 26, 2026 18:40
Comment thread ldk-server/src/api/error.rs Outdated
| NodeError::GossipUpdateTimeout
| NodeError::LiquiditySourceUnavailable
| NodeError::LiquidityRequestFailed
| NodeError::PayerProofCreationFailed

@tankyleo tankyleo Aug 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Codex wants to classify this to an InvalidRequestError rather than an InternalServerError. It's not perfect, but seems InvalidRequestError is more likely if we hit PayerProofCreationFailed

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yeah not perfect mapping, but fixed

@tankyleo tankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I asked Codex to cast a broader net, here's what it found, feel free to dismiss aggressively, but all seemed worth taking a look to me.

Comment thread ldk-server/src/util/metrics.rs
Comment thread ldk-server/src/main.rs
Comment thread docs/api-guide.md
@tankyleo

Copy link
Copy Markdown
Contributor

Also confirmed this patch now keeps my fans quiet on mainnet

@benthecarman
benthecarman requested a review from tankyleo August 27, 2026 02:24
@benthecarman
benthecarman force-pushed the update-ldk-node branch 2 times, most recently from d498240 to 5a1a162 Compare August 31, 2026 17:20
Comment thread ldk-server/src/main.rs Outdated
&event_sender);

if let Some(metrics) = &metrics {
metrics.update_payments_count(true);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Codex found this can race with update_all_pollable_metrics and suggests to remove fn update_payments_count entirely and have update_all_pollable_metrics be the sole writer.

I then asked about the other metrics here's what it had to say:

_ Yes, but not all in the same way.

  - Payment counters had the strongest race: update_all_pollable_metrics() performed an absolute store(), while update_payments_count() performed fetch_add() on the same value. That could permanently double-count or lose an
    update until reconciliation.

  - Balance gauges can still race: both the polling task and payment event handlers call update_all_balances(). Because both write absolute snapshots, an older, slower call could overwrite a newer snapshot. This causes
    temporary staleness, not double-counting, and the next refresh corrects it.

  - Channel metrics avoid this particular race: total_channels_count is event-driven, while public/private channel counts are poll-driven. They do not concurrently update the same atomic value.
  - Peer count is polling-only.

  For complete consistency, balance refreshes should also be serialized or assigned to a single updater. Prometheus gauges usually tolerate brief staleness, whereas payment counters decreasing or double-counting is more
  operationally problematic.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

thanks fixed

Comment thread ldk-server/src/util/metrics.rs Outdated
self.total_pending_payments_count.store(pending_payments_count, Ordering::Relaxed);

let channels_count = node.list_channels().len() as i64;
self.total_channels_count.store(channels_count, Ordering::Relaxed);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Codex suggests we initialize other metrics in addition to the total channels count:

    Initialize poll-only metrics at startup

    Seed peer and channel visibility gauges before the first delayed metrics poll.\n\nAI assistance: OpenAI Codex was used for this change.

diff --git a/ldk-server/src/util/metrics.rs b/ldk-server/src/util/metrics.rs
index 1c92b87..893b1b9 100644
--- a/ldk-server/src/util/metrics.rs
+++ b/ldk-server/src/util/metrics.rs
@@ -133,9 +133,20 @@ impl Metrics {
                        Err(e) => error!("Failed to initialize payment metrics: {e}"),
                }

-               let channels_count = node.list_channels().len() as i64;
-               self.total_channels_count.store(channels_count, Ordering::Relaxed);
+               let all_channels = node.list_channels();
+               self.total_channels_count.store(all_channels.len() as i64, Ordering::Relaxed);
+
+               let public_channels_count =
+                       all_channels.iter().filter(|channel_details| channel_details.is_announced).count()
+                               as i64;
+               self.total_public_channels_count.store(public_channels_count, Ordering::Relaxed);

+               let private_channels_count =
+                       all_channels.iter().filter(|channel_details| !channel_details.is_announced).count()
+                               as i64;
+               self.total_private_channels_count.store(private_channels_count, Ordering::Relaxed);
+
+               self.update_peer_count(node);
                self.update_all_balances(node);
        }

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed, collect all before a delayed poll

Comment thread ldk-server/src/main.rs Outdated
Comment on lines +733 to +734
Ok(None) => error!("Unable to find payment with payment ID: {payment_id}"),
Err(e) => error!("Failed to retrieve payment with payment ID {payment_id}: {e}"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Grok 4.6 found this: should we call event_node.event_handled() in these branches too ?

[bug] ldk-server/src/main.rs:728 _ send_payment_event skips event_handled() when payment-store lookup fails, which can stall the event queue

Then codex:

[P1] Resolve payment events when payment lookup fails _
With an active subscriber, Ok(None) and Err(_) return without calling event_handled().
Since LDK Node repeatedly returns the queue-head event until acknowledged,
a missing or unreadable payment record blocks every later event and creates a tight retry loop.
Emit a partial event with payment: None, or otherwise explicitly acknowledge/drop the event;
use controlled backoff if retrieval errors should be retried.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this is the suggested patch:

diff --git a/ldk-server/src/main.rs b/ldk-server/src/main.rs
index c5fd800..e1fab26 100644
--- a/ldk-server/src/main.rs
+++ b/ldk-server/src/main.rs
@@ -706,27 +706,23 @@ fn send_payment_event(
 ) {
        if event_sender.receiver_count() == 0 {
                debug!("No event subscribers connected, skipping payment event");
-               if let Err(e) = event_node.event_handled() {
-                       error!("Failed to mark event as handled: {e}");
+       } else {
+               match event_node.payment(payment_id) {
+                       Ok(Some(payment_details)) => {
+                               let payment = payment_to_proto(payment_details);
+
+                               let event = payment_to_event(payment);
+                               if let Err(e) = event_sender.send(EventEnvelope { event: Some(event) }) {
+                                       debug!("No event subscribers connected, skipping event: {e}");
+                               }
+                       },
+                       Ok(None) => error!("Unable to find payment with payment ID: {payment_id}"),
+                       Err(e) => error!("Failed to retrieve payment with payment ID {payment_id}: {e}"),
                }
-               return;
        }

-       match event_node.payment(payment_id) {
-               Ok(Some(payment_details)) => {
-                       let payment = payment_to_proto(payment_details);
-
-                       let event = payment_to_event(payment);
-                       if let Err(e) = event_sender.send(EventEnvelope { event: Some(event) }) {
-                               debug!("No event subscribers connected, skipping event: {e}");
-                       }
-
-                       if let Err(e) = event_node.event_handled() {
-                               error!("Failed to mark event as handled: {e}");
-                       }
-               },
-               Ok(None) => error!("Unable to find payment with payment ID: {payment_id}"),
-               Err(e) => error!("Failed to retrieve payment with payment ID {payment_id}: {e}"),
+       if let Err(e) = event_node.event_handled() {
+               error!("Failed to mark event as handled: {e}");
        }
 }

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

done

@benthecarman

Copy link
Copy Markdown
Collaborator Author

Also fixed the proto definition to be a single string for the PageToken and it's handling in the cli.

@tankyleo
tankyleo self-requested a review September 1, 2026 00:08
@benthecarman
benthecarman force-pushed the update-ldk-node branch 2 times, most recently from ffdb37f to fba0e98 Compare September 4, 2026 19:03
Comment thread docs/api-guide.md Outdated
Comment thread docs/api-guide.md Outdated
Comment thread ldk-server-grpc/src/events.rs
Comment thread ldk-server-grpc/src/api.rs
Comment thread ldk-server-grpc/src/types.rs
Comment thread ldk-server/src/util/entropy.rs
Comment thread ldk-server-grpc/src/types.rs
Comment thread ldk-server/src/main.rs Outdated
Comment thread ldk-server/src/api/bolt11_claim_for_id.rs
Comment thread ldk-server/src/main.rs Outdated

@tankyleo tankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One more comment, found with a quick "look for ways to simplify the code" with codex.

Comment thread ldk-server/src/util/metrics.rs Outdated

@tankyleo tankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good to squash thanks, also filed this issue in ldk-node to get clarification on this claimable_amount_msat parameter

use ldk_server_grpc::types::HtlcLocator;
use prost::Message;

// Encoded with the original schema: channel_id = 1, user_channel_id = 2, node_id = 3.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Keep telling my AIs that we can aggressively break compat here since we haven't released yet no ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

oh woops, missed that, will remove, maybe should put in the claude.md for now, but we are so close to release so..

@benthecarman

Copy link
Copy Markdown
Collaborator Author

@tankyleo tankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Worth breaking this PR into a few more commits I think ? No need for any changes to the contents. The comment below is from codex:

  > Could you split this PR into more focused commits? 5ceee90 does considerably more than update the ldk-node dependency: it also changes the hold-invoice RPCs from hash to ID, changes payment-event schemas, renames a public
  > payment field, exposes new channel/HTLC fields, and rewrites payment metrics. Likewise, 738caff combines the payment-storage migration with pagination API changes, event-acknowledgement behavior, new PaymentClaimable
  > data, and unrelated CLI/metrics refactors.
  >
  > These are independently reviewable changes_several affecting public API or wire compatibility_and each should have a commit message explaining its intent. At minimum, please separate the dependency/API adaptation, newly
  > exposed fields, payment storage and pagination migration, event-delivery behavior, and cleanup refactors. The protobuf tag compatibility fix should be kept with the isolated payment-event schema commit.

Update the pinned node API and replace hash-based claim and fail RPCs
with payment-ID requests across the server, clients, CLI, and MCP.
Keep manual handling enabled for unknown BOLT 11 payments.

Adapt fallible payment reads, paginated metric reads, event inputs, and
mnemonic generation to the new dependency. Keep event schemas and the
existing metric writers unchanged here.

AI assistance: OpenAI Codex was used for this change.
Rename Payment.id to Payment.payment_id for consistent naming across
payment requests and responses. This changes generated client fields
and JSON output; protobuf field number 1 remains unchanged.

AI assistance: OpenAI Codex was used for this change.
Add local payment IDs to payment events and expose the claimable
amount for hold-invoice consumers. Explain the existing lower-bound
claim check without changing its optional request parameter.

Number event fields in declaration order, with payment_id first.
Backward compatibility is not required yet.

AI assistance: OpenAI Codex was used for this change.
Return negotiated channel-type features from ListChannels and carry
incoming and outgoing HTLC amounts in forwarding records. Keep HTLC
amounts optional because the node may not know them.

AI assistance: OpenAI Codex was used for this change.
Add payer-proof creation to the gRPC, CLI, and MCP interfaces. Include
the preimage and invoice in successful-payment events because stateless
proof creation requires both values. Document the proof lifecycle and
reject invalid proof inputs as request errors.

AI assistance: OpenAI Codex was used for this change.
Refresh payment counters from paginated node snapshots and remove
concurrent event increments. Initialize all metrics before polling and
delay the first poll to avoid repeating the startup scan.

AI assistance: OpenAI Codex was used for this change.
Read payment history directly from LDK Node and stop duplicating
payment records in the server database. Retain the server store for
forwarded-payment history.

Replace structured page tokens with opaque strings across both listing
APIs and adapt CLI pagination to pass them through. Existing clients
must update for the token schema change.

AI assistance: OpenAI Codex was used for this change.
Acknowledge payment events when no subscriber is connected or required
payment details cannot be read, so the node event queue can continue.
Skip payment lookups when no subscriber is connected.

Document live delivery limits, state reconciliation, unrecoverable
payer-proof inputs, and automatic failure of unclaimed hold payments
at their claim deadline.

AI assistance: OpenAI Codex was used for this change.
Move payment values into event constructors to avoid deep clones and
convert event-only fields only when the constructor is called. Share
channel visibility counts between initialization and polling, deriving
private counts from the same snapshot.

AI assistance: OpenAI Codex was used for this change.
@benthecarman

Copy link
Copy Markdown
Collaborator Author

@tankyleo tankyleo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

thank you!

@benthecarman
benthecarman merged commit f9bf766 into lightningdevkit:main Sep 8, 2026
11 checks passed
@benthecarman
benthecarman deleted the update-ldk-node branch September 8, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants