AI-generated analysisPublished automatically and not human-verified. Validated context appears in community notes below.
← Watch feed
Critical 89 Bitcoin

More AI fixes and defense-in-depth (#3376)

Public commit record

What the developer wrote

Authored by pm47

91/100 · Strong
More AI fixes and defense-in-depth (#3376)

* loupe: add min-to-self-delay check

See: https://github.com/project-loupe/audit-eclair/issues/205

* loupe: use distinct nonces for initial commitments

See https://github.com/project-loupe/audit-eclair/issues/83

* loupe: check signed psbt

See https://github.com/project-loupe/audit-eclair/issues/44

* loupe: prevent overflow on relay-fee calculation

See https://github.com/project-loupe/audit-eclair/issues/31

* Validate relay parameters before on-the-fly funding

When relaying a blinded payment to a wallet node we don't have a channel
with, we cannot validate the relay parameters against a channel_update,
so we skipped that validation entirely before attempting on-the-fly
funding. A malicious wallet node could thus create a blinded path with
a null cltv_expiry_delta for our node, and we would fund a channel and
relay an HTLC that expires at the same block as the upstream HTLC. That
HTLC could then be fulfilled on-chain by the wallet node while the
upstream HTLC times out, making us lose the HTLC amount.

We now validate the relay parameters against the ones the channel we
would create would use (our expiry delta, htlc minimum and our default
relay fees) and fail early instead of funding a
channel for a payment we would have rejected anyway.

See: https://github.com/project-loupe/audit-eclair/issues/162

* Force-close with a publishable commitment

When we sign a splice transaction first (which always happens when we
don't contribute any input) and our peer withholds their tx_signatures,
we cannot publish that splice transaction. Our latest commitment builds
on it, so its commit tx cannot confirm unless our peer publishes the
splice transaction. We still force-closed with that commitment, and the
publisher then retried forever waiting for the parent.

A malicious peer could exploit this to lock our funds indefinitely, or
worse, to steal pending HTLCs: they wait for the upstream HTLC to time
out, then publish the previous commitment and claim the HTLC with its
preimage, since we were unable to time it out on-chain.

When force-closing, we now skip commitments whose funding transaction
hasn't been fully signed and use the latest one that can confirm. The
commitments themselves are left untouched: the closing state now
resolves the commitment matching each published commit txid (ours or
our peer's) wherever it needs commitment-specific data (HTLC signatures,
keys, format), including when restoring after a restart and when
reporting the closed channel. Revoked and future commit txs cannot be
matched and keep using the latest commitment, as before. If our peer
later publishes that splice transaction and it confirms, our commit tx
is double-spent and we force-close again with the latest commitment.

* Bound closing fees proposed by our peer

When we pay the closing fees and our peer sends a closing_signed with a
fee outside of our fee range, we try to converge towards their fee, for
compatibility with implementations that ignore fee ranges. But nothing
bounded that fee: a malicious peer could propose a fee larger than our
balance, we would drop our output and sign a closing transaction that
sends our whole balance to miners, in a single round-trip.

We now refuse fees that exceed what our maximum closing feerate allows
when we pay the fees, and reply with a warning: our peer must propose a
reasonable fee, or we can force-close.

See: https://github.com/project-loupe/audit-eclair/issues/63

* Extract preimage from taproot HTLC-success with annex

When our peer publishes their commit tx and claims an HTLC with an
HTLC-success transaction, we extract the preimage from its witness to
relay it upstream. For taproot channels, we only handled the witness
without an annex, whereas we already handled both cases for
Claim-HTLC-success transactions. If an HTLC-success transaction with an
annex was confirmed, we would fail to extract the preimage and treat
the HTLC as timed out, failing it upstream even though it was fulfilled
downstream.

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

* Use pending signed commitment in canSendFee

When checking that we can afford a new feerate, we were using the spec
of the remote commitment we last received a revocation for, ignoring
the changes we had already signed in a pending commit_sig. Every other
balance check uses the pending commitment when there is one. We could
thus send an update_fee that our peer cannot accept, since they check
it against the commitment we signed, which would make them force-close.

* Bound hidden blinded path fees per payment part

When we hide part of the blinded path fees from the payer, we accept
HTLCs whose amount is smaller than the onion amount by at most those
fees. That bound was computed on the whole invoice amount for every
HTLC, so a payer splitting the payment in several parts could underpay
us by the proportional fee on each part.

The hidden fees are paid on each part, so we now compute the bound on
the part's onion amount. We add a small tolerance since LDK doesn't use
the BOLT 4 formula to compute amt_to_forward inside blinded paths and
rounds down the fee it pays as a sender, which can leave parts short by
a few msat.

* Only accept derivation paths tracked by our wallet

When using the eclair signer, Bitcoin Core only holds a watch-only
wallet built from our BIP84 and BIP86 account xpubs. We verified that
addresses and change outputs it provides belong to us by re-deriving
the public key along the derivation path it gives us, but we accepted
any path. Since Bitcoin Core knows our account xpubs, a malicious node
could provide addresses for deeper or non-standard branches: we would
consider them ours and use them for channel closing scripts, splice
outputs and change, but our wallet would never track them and we would
be unable to find those funds.

We now only accept paths of the form purpose'/coin_type'/account'/{0,1}/i
matching the descriptors we import, with the purpose matching the
address type.

* Check mining fees of wallet-funded interactive txs

When funding an interactive transaction (dual funding or splice), we
let bitcoind select wallet inputs and add a change output. We verified
that the funding output and our outputs were preserved, but never that
the resulting mining fee was sane: interactive-tx validation only
enforces a lower bound on the feerate, and splices don't provide a fee
budget. A malicious bitcoind could thus select inputs worth much more
than needed and skip the change output: all checks would pass and the
difference would be paid to miners once we sign.

We now verify that the mining fee we pay stays close to the target
feerate before announcing our inputs to our peer, like we already do
for single-funded channels.

* Add max-funding-feerate configuration parameter

The feerate we use for funding and splice transactions comes from our
fee estimator, which relies on external data. Nothing bounded that
feerate, so inaccurate or malicious data could make us pay arbitrarily
high mining fees on every channel open or splice. Closing transactions
were already protected by max-closing-feerate.

We add a max-funding-feerate parameter (50 sat/byte by default) that
caps the feerate we use for funding and splice transactions. Operators
can RBF their transactions or increase this value and restart their
node if their transactions don't confirm when the mempool is congested.

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
✓ Descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification✓ Links an issue, advisory, or supporting reference✓ Names security-relevant behavior explicitly
The short version

What changed, and why it matters

This is a large security patch for the Eclair Lightning node that fixes multiple ways an attacker could steal funds, burn money to miners, or lock funds forever. The fixes include: preventing force-closes with un-publishable splice transactions; capping funding/closing miner fees; validating relay fees before opening channels on-the-fly; stopping overflow in fee math; rejecting fake Bitcoin Core change addresses; checking signed PSBTs haven't been altered; extracting payment secrets from newer taproot transactions; and ensuring distinct cryptographic nonces are used when signing commitments.

Recommended action

Upgrade to the patched release as soon as it is available. Operators should review the new max-funding-feerate setting and ensure it is appropriate for current mempool conditions. Nodes running earlier versions are exposed to multiple fund-loss, fund-lock, and fee-burning attacks described in the commit message.

Security signals we found

01

Force-close uses latest publishable commitment to avoid unconfirmable splice commit txs

02

Closing fee bounded by maxClosingFeerate when local node pays fees

03

New max-funding-feerate configuration caps funding/splice miner fees

04

Interactive-tx funding checks mining fee against 1.5x target feerate

05

Derivation paths restricted to known BIP84/BIP86 wallet branches

06

PSBT unsigned transaction txid verified before signing

07

nodeFee overflow fixed with BigInt saturation

08

Blinded path hidden fees validated per payment part

09

On-the-fly funding validates default relay parameters when no channel exists

010

Minimum to_self_delay enforced to prevent zero-delay taproot script failure

011

Distinct signing nonces used for initial taproot commitments

012

Taproot HTLC-success witness with annex now parsed for preimage extraction

013

canSendFee uses pending signed remote commitment instead of revoked one

Risk score

Why this scored 89/100

Our methodology →
Potential impact 28/30
Exploitability 22/25
Stealth signal 12/15
Affected reach 13/15
Confidence 9/10
Evidence quality 5/5
Human-validated context

Community notes

Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.

No validated notes yet.

The AI analysis stands alone for now. Submit a note if you can add evidence or important context.