Port release 26.06.8 to master - #9596
Merged
nGoline merged 9 commits intoOct 2, 2026
Merged
Conversation
invoice's `expiry` parameter is an unclamped param_u64, and far-future values break the daemon in two different ways: - expiry >= 2^60 needs more bits than push_varlen_field() can encode in the bolt11 `x` field, so bolt11_encode() aborts the whole daemon (FATAL SIGNAL 6). - far below that (anywhere past ~584k years), the invoice expiration timer's nanosecond-grain u64 counter overflows: install_expiration_timer() arms a timer that reads as already due, trigger_expiration() finds nothing expired, re-arms, and the daemon busy-loops at 100% CPU with the RPC reply left racing the storm. Refuse at the parameter stage instead: expiry >= 2^32 seconds (~136 years) returns JSONRPC2_INVALID_PARAMS, keeping a wide margin under both limits. 2^32 - 1 still works. Changelog-Fixed: lightningd: fix crash (`FATAL SIGNAL 6`) and a 100% CPU busy-loop when calling `invoice` with an `expiry` too far in the future (now refused above 2^32 seconds). (cherry picked from commit 009caeb)
A crafted bolt11 can carry expiry >= 2^32 past the invoice RPC's gate: the expiry field is a varint and decode does not require a valid signature, so createinvoice fed b11->expiry straight into invoice creation -- hitting the same bolt11_encode() 60-bit abort and expiry-timer overflow as the invoice path. Same bound, same message, placed before the re-encode. Pointed out in the PR thread. Changelog-Fixed: lightningd: refuse bolt11 `expiry` above 2^32 seconds in `createinvoice` too, instead of crashing or busy-looping. Signed-off-by: Amperstrand <amperstrand@localhost> (cherry picked from commit 5f433f0)
The createinvoice expiry test hardcoded the lnbcrt prefix, but
liquid-regtest invoices carry the ert HRP (chainparams), so the
crafted string died at the bech32 parse there ('invalid bech32
string') before the bound check -- the liquid CI arm has been
failing on it. Take the HRP from a real invoice on the running
network instead; both arms pass locally now.
Changelog-None: test-only
Signed-off-by: Amperstrand <amperstrand@localhost>
(cherry picked from commit ebeaf77)
…e commitment With every fee estimate at the floor, the opener's closing fee range is a single value. Each output is rounded down to whole satoshis, so the msat remainders end up in the fee and the closing transaction pays one satoshi more than the fee both sides agreed on. lightningd rejects that transaction as above its maximum, keeps the commitment as last_tx, and the close proceeds to broadcast the commitment while reporting a mutual close. test_closing_fee_rounding_at_ceiling pins both nodes at the floor, leaves remainders of 999 and 1 msat, closes from the opener, and checks that the transaction returned by close is a two-output closing transaction paying the agreed fee plus one satoshi, and that both nodes see the output after one block. Marked xfail until the following commits. Changelog-None. (cherry picked from commit 0471b3b)
closing_fee_is_acceptable checked the fee the closing transaction pays against the maximum derived from the unilateral feerate. That fee is larger than the fee_satoshis both sides agreed on whenever the outputs' msat remainders were rounded away or an output was trimmed as dust. With every estimate at the floor, closingd's minimum and maximum are the same value, so any rounding put the transaction one satoshi over the maximum and lightningd rejected it. The rejection left the commitment as last_tx and the close broadcast it while reporting a mutual close. Derive the negotiated fee from the transaction: our rounded-down balance minus the output paying our shutdown script. closingd subtracts fee_satoshis from the opener's output, and the maximum only applies when we are the opener, so that difference is exactly the fee we agreed to pay. Bound that instead. The minimum is still checked against the fee the transaction actually pays, which is what relay depends on. If our output was trimmed the whole fee is ours and is bounded as before. Changelog-Fixed: lightningd: a mutual close at the fee ceiling no longer falls back to broadcasting the commitment because of satoshi rounding. Fixes: ElementsProject#9495 (cherry picked from commit be5b625)
…ling back to the commitment closingd negotiates within the feerange given to `close`, and lightningd hands it the range minimum as its floor. lightningd's own acceptance check does not use that minimum: it checks the agreed fee against the floor derived from its fee estimates. When the estimates sit above the range, the agreed fee is rejected as too low, the commitment stays as last_tx, and the close broadcasts it while reporting a mutual close. test_closing_feerange_below_estimates opens at the floor, raises the opener's estimates, closes with a feerange pinned at the floor, and checks that the transaction returned by close is a two-output closing transaction at the agreed fee. Marked xfail until the following commit. Changelog-None. (cherry picked from commit 9c0b9c2)
…g fee peer_start_closingd hands closingd the minimum of the feerange given to `close` as its floor, so closingd negotiates down to it. The acceptance check in closing_fee_is_acceptable kept using the floor derived from the fee estimates, so with estimates above the range the agreed fee was rejected as too low, the commitment stayed as last_tx, and the close broadcast it while reporting a mutual close. Use the range minimum as the floor there too, as calc_max_close_feerate already does for the maximum. Both ends of the check now match the bounds closingd negotiated within. Changelog-Fixed: lightningd: `close` with a feerange below the fee estimates no longer falls back to broadcasting the commitment. (cherry picked from commit 118bf9c)
…cted When lightningd rejects a closing fee, closingd never learns it: the reply only carries a txid, which closingd uses for its billboard. It agrees to the offer, reports the close complete, and lightningd broadcasts last_tx, still the commitment, as if it were the mutual close. The previous commits removed the known reasons for a rejection, so add --dev-reject-closing-fee, which makes lightningd reject every closing fee the peer offers. test_closing_rejected_fee_fails_negotiation runs it on the non-opener: the opener's close must end in a unilateral close at its timeout, and the rejecting node must never agree to a fee or broadcast anything. Marked xfail until the following commit. Changelog-None. (cherry picked from commit 24d4335)
…sing fee lightningd's reply to closingd_received_signature carried only a txid. closingd used it for the billboard and went on to agree to the offer, so when lightningd had rejected the fee the close still completed and drop_to_chain broadcast last_tx, which was still the commitment, while the close command reported a mutual close. Add the verdict to the reply. On a rejection lightningd logs it at UNUSUAL, and closingd sends the peer a warning and exits instead of agreeing, the same way it handles a fee range with no overlap. The channel stays in CLOSINGD_SIGEXCHANGE: negotiation restarts on reconnect with lightningd's current bounds, and the close command's timeout decides when to close unilaterally. Changelog-Fixed: lightningd: a closing fee lightningd rejects fails the negotiation instead of broadcasting the commitment as a mutual close. (cherry picked from commit a944beb)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.