Skip to content

Cut v0.7.1 release - #1158

Merged
tnull merged 29 commits into
lightningdevkit:release/0.7from
tnull:2026-10-backports-0.7.1
Oct 12, 2026
Merged

tnull merged 29 commits into
lightningdevkit:release/0.7from
tnull:2026-10-backports-0.7.1

Conversation

@tnull

@tnull tnull commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator

We add a bunch of backports and security hardening fixes and cut v0.7.1 patch release.

tnull and others added 29 commits October 12, 2026 13:53
Previously, seed files were created using `fs::File::create()` which
inherits the default umask, potentially making the sensitive seed
material world-readable on Unix systems.

This change:
- Creates seed files with mode 0o400 (owner read only) on Unix
- Uses `create_new` instead of `create` to atomically fail if the file
  already exists, providing defense-in-depth against TOCTOU race
  conditions

Co-Authored-By: Claude AI

Backport of e93844a
Conflicts Resolved in src/io/utils.rs
Move the GC pass from after insertion to before, so that stale entries
are reclaimed before allocating a new bucket. This avoids unnecessary
growth of the user map between GC cycles.

AI tools were used in preparing this commit.

Backport of 818cccd
Previously, we'd attempt to decode any hex string even if it had an odd
number of input characters. Here, we check the length and return `None`
in case it's odd.

Signed-off-by: Elias Rohrer <dev@tnull.de>

Backport of 8189984
The check that an LSP didn't withhold more than the agreed-upon JIT
channel fee only ran for inbound payments we already had a record for.
BOLT12 offer and spontaneous payments only get their record created
inside the claim handler, so they skipped the check entirely and were
claimed no matter how much the counterparty skimmed.

Refuse any withheld fee for payments without a stored record, as we
never authorized a skim for them. This matches the behavior on main,
where the check moved to the authenticated BOLT11 payment metadata.
Only this part of the upstream change is backported.

Backport of 44146f9
Conflicts Resolved in src/event.rs

Co-Authored-By: HAL 9000
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

Backport of ca4d4c3
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

Backport of 9ab2106
Conflicts Resolved in src/payment/unified_qr.rs, tests/integration_tests_rust.rs
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

Backport of 187056b
Build the updated object separately and persist it before replacing the cached entry, so failed writes leave memory aligned with storage.

This finding was discovered by Project Loupe

AI-Assisted-By: OpenAI Codex

Backport of 6560dff
Conflicts Resolved in src/data_store.rs
Apply updates to a cloned object and only replace the cached entry after the backing store write succeeds.

This finding was discovered by Project Loupe

AI-Assisted-By: OpenAI Codex

Backport of e3793b8
Conflicts Resolved in src/data_store.rs
Check for the object first, remove it from the backing store, and only then delete it from the in-memory map.

This finding was discovered by Project Loupe

AI-Assisted-By: OpenAI Codex

Backport of 531d52d
Conflicts Resolved in src/data_store.rs
We stop requiring that splice-ins leave an anchor reserve for an
additional anchor channel on top of the existing set of anchor channels;
after splice-ins, our anchor reserve only needs to cover the existing
set of anchor channels.

Backport of 4c8ecb2
Conflicts Resolved in src/lib.rs
When we are preparing to open a channel to a peer, we should reserve
onchain funds for an anchor channel when the peer's init features
signals anchor channels as optional, as channel negotiation with such a
peer can result in an anchor channel.

Tests written with codex.

Backport of 2c2c287
Conflicts Resolved in src/lib.rs, src/liquidity.rs, tests/integration_tests_rust.rs
Backport of 0757faa
Conflicts Resolved in src/chain/bitcoind.rs
Bitcoin Core's REST headers endpoint cannot serve headers from a stale
branch. This prevented REST-backed nodes from finding a common ancestor
and following the replacement chain after a reorg.

In lightningdevkit#1006 we enabled bitcoind REST chain source support in CI, which
exposed this failure.

Co-Authored-By: HAL 9000

Backport of 66d48d2
Conflicts Resolved in src/chain/bitcoind.rs, tests/reorg_test.rs
Payment preimages are proof-of-payment and correlation material. The INFO
log previously included the full preimage after a successful outbound
payment, exposing it to log collectors and anyone with log access.

Remove the preimage from the 'Successfully sent payment' log message in
src/event.rs, retaining only the payment hash for diagnostics. The
preimage remains accessible via the PaymentSuccessful event and payment
store APIs.

Backport of 8828e52
Conflicts Resolved in src/event.rs
`ElectrumRuntimeStatus::stop` reset itself to `Stopped` with empty pending
registration vectors, dropping the `ElectrumRuntimeClient` and, with it, the
`ElectrumSyncClient` that owns the registered transactions and outputs.
`start` then drained the pending vectors, so nothing survived even a single
cycle.

As `ChannelMonitor`s only register their watched transactions and outputs
while being loaded in `Builder::build`, a `stop`/`start` cycle that doesn't
rebuild the node left the chain source with no registrations at all, i.e., the
node would no longer learn about confirmations or spends of, e.g., its funding
outputs. Note we can't simply re-register from
`ChannelMonitor::load_outputs_to_watch` on `start` either, as, e.g., the
`OutputSweeper` also registers outputs it wants to see spent.

Here we therefore keep a canonical, deduplicated registration inventory in
`ElectrumRuntimeStatus` that is maintained whether we're started or not:
`register_tx`/`register_output` always record the entry and additionally
forward it to the client if one is live, `start` replays the inventory to the
fresh client without consuming it, and `stop` merely drops the client.

This also restores parity with the Esplora chain source, whose `tx_sync` is
long-lived and hence never loses its registrations.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Backport of af634d3
Conflicts Resolved in src/chain/electrum.rs
Only a missing ChannelManager should create a fresh node. Propagate
other storage errors so transient failures cannot replace live channel
state with an empty manager.

Fixes lightningdevkit#1026

Co-Authored-By: HAL 9000

Backport of 03dafa9
Conflicts Resolved in src/builder.rs
Mempool reconciliation checks every cached transaction against the raw
mempool response. Using a Vec to store the raw mempool txids makes each
membership check linear and the retention pass quadratic. Store the
txids in a HashSet to make membership checks expected constant time and
reconciliation expected linear.

Co-Authored-By: HAL 9000

Backport of 8c632e5
Conflicts Resolved in src/chain/bitcoind.rs
…ayments

UnifiedPayment::send previously treated any error from the BOLT11 leg
of a unified payment as non-terminal and fell through to the on-chain
payment method. This meant a retried BOLT11 payment that returns
Error::DuplicatePayment would still result in an on-chain transaction
being broadcast for the same invoice — a duplicate payment.

Error::DuplicatePayment is now terminal in UnifiedPayment::send: the
unified payment aborts instead of falling back to on-chain.

Fixes lightningdevkit#1033.

unified_send_receive_bip21_uri already funds two nodes, opens a
channel, and sends a successful BOLT11 payment via uri_str_without_offer
partway through. Add the regression assertion right there — retry the
same uri_str_without_offer and assert DuplicatePayment, not a new
on-chain payment — instead of duplicating that setup in a standalone
test.

Backport of 9c271d7
Conflicts Resolved in src/payment/unified_qr.rs, tests/integration_tests_rust.rs
In UnifiedPayment::send, the BOLT11 leg's bolt11_invoice.send only
returns Err(PersistenceFailed) *after* pay_for_bolt11_invoice has
already succeeded and the Lightning payment is in-flight. The previous
match treated every remaining error as a fall-through to the next
payment method, so a persistence failure after initiation would
broadcast an on-chain transaction for the same URI — a duplicate
payment.

Err(Error::PersistenceFailed) on the BOLT11 leg is now terminal,
mirroring how DuplicatePayment is handled, and aborts the unified
payment instead of falling back to on-chain.

This is a regression hazard raised during review of the DuplicatePayment
fix (PR lightningdevkit#1038). It is pre-existing and orthogonal to lightningdevkit#1033; tracked
here as the unified variant of the broader post-commit persistence
hazard.

Per review discussion: rather than a dedicated node/channel fixture,
build unified_send_receive_bip21_uri's node_a on a PaymentFailingStore
(inert until armed) from the start, and add a PersistenceFailed
assertion at the end of that test using a fresh BOLT11-only URI. This
reuses the funding/channel/announcement setup and the successful-send
flow the test already has, rather than duplicating it.

Adds PaymentFailingStore (a KVStore wrapper that fails writes to the
payments namespace on demand) and setup_two_nodes_with_failing_store_a
(mirrors setup_two_nodes, but node_a is built on PaymentFailingStore).

Backport of d7ee094
Conflicts Resolved in src/payment/unified_qr.rs, tests/common/mod.rs, tests/integration_tests_rust.rs
Estimating the maximum funding amount, the maximum splice-in amount, or
a send-all payment builds a temporary drain transaction whose anchor
reserve output pays to the wallet's first internal address, then cancels
it as though it were a real transaction. A drain transaction never
reserves a change address, so the cancel has nothing of its own to undo
and instead clears the usage mark on that internal address. If a pending
channel funding or splice contribution had reserved the same address for
its change, the wallet could hand it out again until the pending
transaction is synced, causing address reuse.

Fee estimates now leave the wallet untouched, since BDK neither marks an
address nor locks an input for a transaction that drains to a fixed
script.

Co-Authored-By: Claude <noreply@anthropic.com>

Backport of 7880030
Conflicts Resolved in src/wallet/mod.rs
…istenceFailed errors

The BOLT12 arm used .map_err() + if let Ok, so DuplicatePayment and
PersistenceFailed fell through to BOLT11/onchain — risking double payment.
Now treats both as terminal, mirroring the BOLT11 fix in lightningdevkit#1038.

Fixes: lightningdevkit#1060

AI-assisted: generated by Sera (Hermes Agent), verified manually.
Signed-off-by: Bartok9 <259807879+Bartok9@users.noreply.github.com>

Backport of 3c98b1c
Conflicts Resolved in src/payment/unified_qr.rs
Ignore PaymentFailed when an outbound Lightning payment has already
succeeded. Check and update the record atomically, preserving its metadata
and suppressing the contradictory failure notification.

Continue emitting failure events for unchanged or missing records so
replay can recover from event-queue persistence failures.

Developed with assistance from OpenAI Codex.

Backport of 1bb6aa3
Conflicts Resolved in src/event.rs, src/payment/store.rs
Conservatively include channels whose type is not yet known when
calculating the anchor reserve. Otherwise, opening or accepting new
channels, or splicing funds into existing channels, can leave too little
reserved for channels still being negotiated.

Do not count channels with unknown channel types when assessing whether
0FC support is required from the chain source. This part is all
user-controlled, and we do not want to require the chain source to support
0FC if startup happens with an in-flight channel open, and no other 0FC
chain source support predicates are true.

Reported by Project Loupe

Backport of 87c3182
Conflicts Resolved in src/lib.rs
If we have sent an outbound payment with a given `payment_hash` and
we receive an inbound payment with a matching `payment_hash`, its
possible this is an indication of a payment loop, but its also
possible its simply a random HTLC with the same hash. We should,
thus, not update the outbound payment's status, as it might still
be pending and complete. We can detect the payment's failure when
the HTLC failure comes back to us as the sender.

Backport of c49b86d
Conflicts Resolved in src/event.rs
If we have already received a payment and marked it as
`PaymentStatus::Succeeded` before receiving a second HTLC with the
same `payment_hash`, we should leave the payment at
`PaymentStatus::Succeeded` rather than reverting it to failed.

Backport of a176d51
Conflicts Resolved in src/event.rs
`ChannelManager::claim_funds` fails if there are any custom even
TLVs. While this is documented, its somewhat deadly for swap
services - they might release the preimage in response to a
`PaymentClaimable` event and assume they can definitely claim the
payment, but LDK will refuse to do so.

The `lightning` crate really needs to fix this API for the next
release, but for now we should avoid having such an obvious API
hole in `ldk-node`.

Backport of 8dd1c91
Conflicts Resolved in src/event.rs
BOLT11 JIT invoices did not enforce their advertised amount at the
recipient. A sender could bypass the LSP's intercept SCID and have a
smaller payment accepted over an existing channel.

Require the full sender-declared invoice amount for generated and
caller-supplied payment hashes. Preserve negotiated LSP fee deductions
and cover both invoice paths with an underpayment regression.

Fixes lightningdevkit#609

Co-Authored-By: HAL 9000

Backport of b5c731d
Conflicts Resolved in src/liquidity.rs, tests/integration_tests_rust.rs
Bump the crate and bindings version numbers to v0.7.1 and add a
CHANGELOG section listing the fixes backported for this patch release.

Co-Authored-By: HAL 9000
@tnull
tnull requested a review from joostjager October 12, 2026 12:48
@ldk-reviews-bot

ldk-reviews-bot commented Oct 12, 2026 •

Copy link
Copy Markdown

👋 Thanks for assigning @joostjager 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.

@tnull
tnull merged commit edc8916 into lightningdevkit:release/0.7 Oct 12, 2026
14 of 17 checks passed
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.