Skip to content

Sync main with upstream lightningdevkit/ldk-node (86 commits)#4

Open
tonible14012002 wants to merge 86 commits into
lamtuanvu:mainfrom
tonible14012002:main
Open

Sync main with upstream lightningdevkit/ldk-node (86 commits)#4
tonible14012002 wants to merge 86 commits into
lamtuanvu:mainfrom
tonible14012002:main

Conversation

@tonible14012002

Copy link
Copy Markdown

What

Fast-forwards `lamtuanvu/ldk-node:main` to the current upstream `lightningdevkit/ldk-node:main` (`f2e44fd`).

This is a clean fast-forward — `lamtuanvu/main` (`a2c34cb`) is a strict ancestor of upstream HEAD and carries no proprietary commits, so there are zero conflicts to resolve.

Why

The fork's `main` had drifted 86 commits behind upstream. Bringing it current is a prerequisite for cleanly rebasing feature branches (e.g. the `swaps` work) onto a modern base instead of the ~1-year-old July 2025 fork point.

What's included (upstream batch, newest first)

86 commits total, a2c34cb..f2e44fd.

Verification

git merge-base --is-ancestor a2c34cb f2e44fd   →  YES (clean fast-forward)

tnull and others added 30 commits June 10, 2026 11:05
Validate splice-out requests against outbound capacity after converting the requested satoshi amount to millisatoshis with overflow handling. This prevents values above the spendable channel balance from slipping past the guard due to a unit mismatch.

Keep splice integration coverage aligned with the corrected capacity semantics by rejecting an amount one satoshi above outbound capacity and deriving the full-cycle splice-out amount from the channel's current spendable capacity.

AI-Assisted-By: OpenAI Codex

Co-Authored-By: HAL 9000

This finding was discovered by Project Loupe
A cancellable task spawned during shutdown could otherwise outlive the shutdown sequence instead of being cancelled with the rest of the cancellable runtime work. Reject late spawns while shutdown is draining tasks and reopen that path when a stopped node starts again.

Co-Authored-By: HAL 9000

This finding was discovered by Project Loupe
Replace per-protocol single-LSP configuration `LSPS1Client` and `LSPS2Client`
with a unified `Vec<LspNode>` model where users configure LSP nodes via
`add_liquidity_source()` at build time or runtime and per-LSP protocol
support is discovered via the LSPS0 `list_protocols`.

- Introduce a per-LSP `trust_peer_0conf` flag to `LspConfig`/`LspNode`
  structs that controls whether 0-conf channels from that LSP are accepted
- Add LSPS0 protocol discovery `discover_lsp_protocols` with event
  handling for `ListProtocolsResponse`
- Update events to also use each LSP's `trust_peer_0conf` flag
  when deciding whether to allow 0-conf channels
- Replace `set_liquidity_source_lsps1` and `set_liquidity_source_lsps2`
  builder methods with a single `add_liquidity_source()` that takes a
  `trust_peer_0conf` flag
- Rename `set_liquidity_provider_lsps2` to `enable_liquidity_provider`
- LSPS2 JIT channels now query all LSPS2-capable LSPs and automatically
  select the cheapest fee offer across all of them
- Spawn background discovery task on `Node::start()` and expose a watch
  channel so dependent flows can wait for discovery to complete
- Add a new `Liquidity` handler `Node::liquidity()` exposing `add_liquidity_source()`
  API for adding LSPs at runtime, and `lsps1()` for the existing LSPS1 surface
Remove the `Ignoring` variant now that the liquidity source is
always built, so the enum and its match arms are now pure
overhead. Replace it with a struct that holds the `LiquiditySource`
directly and have each trait method delegate straight to
`liquidity_manager()`.
…cellable-shutdown

Prevent late cancellable runtime tasks during shutdown
…out-capacity-units

Reject oversized splice-out amounts
Clarify that public APIs remain unstable before 1.0 while persisted
node state is intended to remain readable by newer releases.

Co-Authored-By: HAL 9000
Add a downgrade canary that writes current node state through the legacy
v1 filesystem store and reopens it with ldk-node v0.7.0. This monitors
whether serialized node, channel, and payment state remains usable by
v0.7.0, including a restored channel and a post-restart payment.

This does not assert that the current filesystem-store v2 IO layout can
downgrade to v0.7.0's v1 layout. That IO-layer downgrade is unsupported:
v2 stores empty namespaces under [empty], which v1 readers do not look
up.

Co-Authored-By: HAL 9000
…antees-downgrade-070

Document compat. guarantees, monitor serialization compat
Refactor liquidity source to support multiple LSP nodes
Electrum transaction sync now reuses the client already shared by BDK
and direct Electrum calls. This avoids opening a second Electrum
connection and completes the reuse intended by lightningdevkit#488.

Co-Authored-By: HAL 9000
…ctrum-client-reuse

Reuse Electrum client for transaction sync
Switches vss-client-ng to the crates.io 0.6 release.

Generated with OpenAI Codex.
Move repeated VssStore construction logic into a shared
build_vss_store() helper and have existing tests use it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract the single-page VSS listing logic into a list_keys method
that accepts page_token and page_size parameters. list_internal
now drives the pagination loop itself, calling list_keys per page.

This prepares for PaginatedKVStore support which will reuse
list_keys for single-page queries.

This also fixes a potential issue where if the VSS server returned None
for the page token we could enter into an infinite loop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Share the common BOLT11 payment send flow between fixed-amount and
explicit-amount sends so follow-up API variants can reuse the same
payment-store and error handling path.

AI-Tool-Disclosure: Created with OpenAI Codex.
Add a BOLT11 payment API for sending less than the invoice amount while
using the invoice amount as the declared total MPP value. Cover the path
with an integration test where two nodes each pay half of one invoice and
the receiver claims the full amount.

AI-Tool-Disclosure: Created with OpenAI Codex.
Return InvalidAmount when converting the requested satoshi amount to
millisatoshis would overflow. This keeps debug and release behavior
consistent and avoids producing a URI whose on-chain amount differs from
its Lightning payment amount.

This commit was created with assistance from OpenAI Codex.

This finding was discovered by Project Loupe
Use saturating arithmetic when accounting for skimmed JIT-channel fees
while validating manually claimed payments. This prevents an oversized
skimmed fee from underflowing the expected claimable amount.

This commit was created with assistance from OpenAI Codex.

This finding was discovered by Project Loupe
Track registered transaction IDs in a set so repeated filter
registrations do not grow the collection or slow block-connected checks.
This keeps the wallet's registered-transaction lookup bounded by unique
transaction IDs.

This commit was created with assistance from OpenAI Codex.

This finding was discovered by Project Loupe
tnull and others added 30 commits June 26, 2026 07:21
Keep pending payment namespace constants next to the primary payment
store constants.

This keeps related persistence keys discoverable together.

Co-Authored-By: HAL 9000
Stop exporting the pending payment index record from the public
payment module.

The pending index is an internal persistence detail and should not
become public API before this ships.

Co-Authored-By: HAL 9000
RBF can spend fee increases from the original transaction's change
output.

Check the replacement fee increase against the current anchor-channel
reserve before signing. This prevents high manual fee rates from
consuming funds reserved for anchor spends.

This finding was discovered by Project Loupe.

Co-Authored-By: HAL 9000
…nsert-or-update

Persist datastore changes before caching
DataStore persistence failure tests use FailingStore through
DynStoreWrapper. That wrapper now requires paginated store
support, so make the helper fail paginated listings the same way it
fails the other store calls.

Co-Authored-By: HAL 9000
…re-rebase-conflict

Fix DataStore failing store pagination
This was unimplemented for the sqlite kv store. Useful if the user
wants to migrate to a different database and also in tests so we don't
have to re-init and setup a node.

AI-assisted-by: OpenAI Codex
This was unimplemented for the postgres kv store. Useful if the user
wants to migrate to a different database and also in tests so we don't
have to re-init and setup a node.

AI-assisted-by: OpenAI Codex
Extract the existing obfuscated key selection so later VSS listing
changes can reuse it without changing the parsing behavior.

AI-assisted-by: OpenAI Codex
This was unimplemented for the VSS kv store. Useful if the user wants
to migrate to a different database and keeps VSS aligned with the other
persistent stores.

AI-assisted-by: OpenAI Codex
This was unimplemented for the in-memory kv store. Useful in tests so
we can migrate data across all supported store implementations.

AI-assisted-by: OpenAI Codex
…ment-store-followups

Bump BDK, address pending payment store follow ups
On-chain payment records don't capture what a transaction was for -- a
channel open, splice, close, sweep, or a plain send. Record that
classification on each on-chain payment, derived from the type LDK
reports when broadcasting the transaction, so it survives restarts
alongside the payment.

The tag keeps only which channels a transaction relates to; amounts and
fees stay on the payment. Existing records keep decoding unchanged.

Compatible with the on-chain transaction classification proposed in lightningdevkit#791.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Record channel-open and splice funding transactions as on-chain payments
at broadcast, and carry them to Succeeded through ANTI_REORG_DELAY
confirmations like any other on-chain payment, instead of tying their
status to the Lightning channel lifecycle. A splice's recorded amount and
fee are this node's share of the funding contribution, which wallet sync
preserves rather than overwriting with its own view of the (possibly
multi-party) transaction.

On-chain RBF of these payments is rejected: LDK drives funding and splice
transactions, so replacing one would broadcast a transaction it isn't
tracking and, for a splice, can't re-sign.

Addresses review feedback to keep on-chain payment status confirmation-
driven rather than gated on ChannelReady.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`create_payment_from_tx` duplicated the amount/fee/direction derivation
that `onchain_payment_fields` already performs. Share it via a helper
that operates on the already-locked wallet, so both paths agree by
construction.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A splice's funding transaction can be stuck at too low a fee rate with no
way to raise it: on-chain RBF is rejected for funding transactions, and
re-issuing splice_in / splice_out errors while a splice is already
pending. Add bump_channel_funding_fee, which replaces the pending splice's
funding transaction at a higher fee rate while preserving its amount and
destination, and point the "a prior splice contribution is pending" errors
at it.

Replacing the transaction also requires signing a funding input the wallet
already treats as spent by the splice being replaced, which it would
otherwise skip after syncing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cover the wallet-event-driven funding payment lifecycle end to end: a
channel-open funding payment reaches Succeeded from wallet sync alone,
asserted before any ChannelReady event is drained to show payment status no
longer depends on the channel-ready signal; and a splice fee-bumped via RBF
stays a single on-chain payment that follows the winning candidate while
keeping its interactive-funding classification across the replacement.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A splice funding payment can be fee-bumped via RBF, producing several
candidate transactions with increasing fees. The payment recorded the
last-broadcast candidate's amount and fee and kept them on confirmation, but
the candidate that actually confirms need not be the last one broadcast — so
an earlier, lower-fee candidate confirming left the payment over-reporting
its fee.

Record each candidate's amount and fee, keyed by txid, so that on
confirmation the payment reflects the candidate that actually confirmed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
splice_channel only checked the splice-out fee; also assert it is recorded
as a confirmed interactive-funding payment. Add a test that a confirmed
splice payment returns to unconfirmed when its block is reorged out,
exercising the unconfirm path for funding payments.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributing to an already-pending splice — e.g. adding our funds to a
counterparty-initiated splice via splice_in or splice_out — replaces the
in-flight funding transaction, so the funding template requires at least the
RBF minimum feerate. We passed our plain ChannelFunding feerate estimate, which
can sit below that minimum (it does at the regtest floor), so the contribution
was rejected with FeeRateBelowRbfMinimum.

Raise the contribution feerate to the template's RBF minimum when one applies,
capped by our max, so it can replace the pending splice. A node can therefore
now contribute to a counterparty's pending splice; the rbf_splice_channel check
that expected splice_out to fail while a splice was pending relied on this very
bug and is dropped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 1.5x-of-estimate funding feerate ceiling was open-coded identically
in splice_in and splice_out. Route both through a max_funding_feerate
helper and keep it, alongside rbf_splice_feerates, in fee_estimator.rs
so the splice funding-feerate policy lives in one place.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
In a splice, both channel parties broadcast the funding transaction,
and the tests drive a single shared bitcoind, so the counterparty's
broadcast can surface it to this node's wallet sync before this node's
own funding classification has run. Under parallel test execution that
classification can lag far enough behind for the sync to record the
transaction as a plain on-chain payment, failing the funding-payment
assertions.

Wait for the funding broadcast to be classified before each affected
splice test syncs its wallets. This is test-only: on a real node the
classification runs locally, well ahead of a counterparty's broadcast
arriving over the network, so the race does not occur.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Creates a node with LN and on-chain state and then randomly migrates
through all the KV store options and checks it still has its state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ayment-using-tx-type

Add splice RBF support
Implement `MigratableKVStore` for all KV Stores
When a fresh node's bitcoind RPC/REST chain source fails to return the
current chain tip, we previously silently fell back to the genesis
block as the wallet birthday. The next successful startup would then
force a full-history rescan of the whole chain. Instead, return a new
BuildError::ChainTipFetchFailed on the first build so the
misconfiguration surfaces immediately and no stale fresh state is
persisted.

Restarts with a previously-persisted wallet are unaffected: a
transient chain source outage on an existing node still allows startup
to proceed. Esplora/Electrum backends currently never expose a tip at
build time so the guard only fires for bitcoind sources; the latent
wallet-birthday-at-genesis issue on those backends is left for a
follow-up.

Co-Authored-By: HAL 9000
Allow Bitcoin Core RPC and REST chain-source configuration to specify
the wallet birthday height used when creating a fresh wallet. This lets
restored wallets rescan from a known height, including genesis, without
overloading a global recovery toggle.

Reject requested heights above the current chain tip with an explicit
build error before wallet state is created. Existing wallets are not
rewound by this option because a safe rewind must invalidate persisted
wallet and LDK state before replaying blocks.

Co-Authored-By: HAL 9000
Let Esplora and Electrum sync configs request BDK full scans
until one succeeds. This keeps recovery scans retryable after
transient sync failures while preserving normal incremental syncs
once recovery has completed.

Co-Authored-By: HAL 9000
…rst-startup-tip-fetch-failure

Allow wallet imports from arbitrary heights, forcing full scans
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.

8 participants