34 KiB
Release Notes
- Bug Fixes
- New Features
- Improvements
- Technical and Architectural Updates
- Contributors (Alphabetical Order)
Bug Fixes
-
The remote-signer PSBT prep now accepts zero-value entries from a sign descriptor's
PrevOutputFetcherwhen populatingWitnessUtxoon non-signed inputs. This unblocks signing flows that reference virtual prev outputs whose value is mandated to be zero, most notably BIP-322'sto_spendoutput (the prev of input 0 of every BIP-322to_signtransaction). Previously the prep stage silently skipped such inputs and the resulting PSBT was rejected downstream bywalletkit.SignPsbtwithinput (index=N) doesn't specify any UTXO info. -
Fixed
OpenChannelwithfund_maxto use the protocol-level maximum channel size instead of the user-configuredmaxchansize. Themaxchansizeconfig option is intended only for limiting incoming channel requests from peers, not outgoing ones.
-
Chain notifier RPCs now return the gRPC
Unavailablestatus while the sub-server is still starting. This allows clients to reliably detect the transient condition and retry without brittle string matching. -
Fixed TLV decoders to reject malformed records with incorrect lengths. TLV decoders now strictly enforce fixed-length requirements for Fee (8 bytes), Musig2Nonce (66 bytes), ShortChannelID (8 bytes), Vertex (33 bytes), and DBytes33 (33 bytes) records, preventing malformed TLV data from being accepted.
-
Fixed
MarkCoopBroadcastedto correctly use thelocalparameter. The method was ignoring thelocalparameter and always marking cooperative close transactions as locally initiated, even when they were initiated by the remote peer. -
Fixed a panic in the gossiper when
TrickleDelayis configured with a non-positive value. The configuration validation now checksTrickleDelayat startup and defaults it to 1 millisecond if set to zero or a negative value, preventingtime.NewTickerfrom panicking.
-
Fixed
lncli unlockto wait until the wallet is ready to be unlocked before sending the unlock request. The command now reports wallet state transitions during startup, avoiding lost unlocks during slow database initialization. -
Fixed handling of BOLT 1 pings requesting 65532 or more pong bytes. LND now ignores these valid no-reply pings instead of disconnecting peers, restoring compatibility with implementations that pad
channel_reestablishmessages with them. -
Fixed
FundingPKScriptto honor the taproot feature bit on v1 channel edges. Private taproot channels stored as v1 gossip objects with the taproot staging feature bit were having their funding scripts incorrectly reconstructed as legacy P2WSH multisig. This affected read paths such asChannelView, which rebuilds the chain watch filter on restart. This was a pre-existing bug since private taproot channels were first introduced. -
Fixed a shutdown race in the channel link that could deadlock the invoice registry during concurrent peer disconnect. The link now waits for
htlcManagerto fully exit before tearing down hodl subscriptions and the hodl queue, preventing orphaned subscriptions from blocking invoice resolution. -
Fixed two follow-ups to the production taproot channels work. The RPC channel acceptor switch now maps
SIMPLE_TAPROOT_FINAL(with every combination of thescid-alias/zero-confmodifiers) so final-taproot opens are reported to external acceptor clients with the correct commitment type instead ofUNKNOWN_COMMITMENT_TYPE. The taproot RBF cooperative-close auto-enable is also narrowed to skip taproot-overlay channels, since the RBF close state machine does not yet thread through theAuxCloserhook that overlay channels rely on to build aux-aware close transactions. -
Fixed
EstimateRouteFeeto use independent probe payment hashes when probing multiple LSPs, preventing later probes from reusing the first probe's CLTV delta. -
Restored insta-dispatch of
CLOSED_CHANNELon the first confirmation of a cooperative close. After the multi-conf reorg-aware close dispatch landed,SubscribeChannelEventsno longer emittedCLOSED_CHANNELuntil the full required confirmation depth was reached. The chain watcher now fires an earlyCLOSED_CHANNELevent over the channel notifier as soon as the coop close spend lands on chain, restoring the v0.20.1 behavior, while the channel arbitrator suppresses the duplicate event that would otherwise be emitted fromMarkChannelClosedat the final confirmation depth.
New Features
- Basic Support for onion
messaging forwarding.
This adds a new message type,
OnionMessage, comprising a path key and an onion blob. It includes the necessary serialization and deserialization logic for peer-to-peer communication.
Functional Enhancements
-
Added pathfinding support for routing onion messages. The router can now find paths through the channel graph specifically filtered for nodes that advertise support for onion messaging (feature bit 38/39).
-
Added fast initial sync for
neutrino-backed nodes via header import. On first startup, the neutrino backend can now bootstrap its block and filter header chains from a local file or HTTP(S) URL instead of fetching them over P2P, dramatically reducing the time-to-sync on fresh installs (minutes instead of hours on mainnet). After the import completes, the node transitions to the normal P2P sync path to catch up to chain tip and stay current. The feature is gated on the two newneutrino.blockheaderssourceandneutrino.filterheaderssourceoptions, which must be specified together; the import source already validates header linkage and proof-of-work, so contextual timestamp checks are skipped during import to accommodate rapidly-mined regtest/simnet headers. See docs/neutrino_headers_import.md for the supported source URLs (e.g.block-dn.orgfor mainnet), file format, and operator guidance. -
Added reorg protection for channel closes. Previously, channel closes were considered final immediately on spend detection with no confirmation waiting. Now, all channel closes require between 3 and 6 confirmations, scaled linearly with channel capacity up to the maximum non-wumbo channel size (~0.168 BTC), with wumbo channels always requiring 6 confirmations.
-
Added support for production (final) simple taproot channels using the finalized taproot channel scripts with feature bits 80/81. Production taproot channels use optimized scripts (
OP_CHECKSIGVERIFYinstead ofOP_CHECKSIG+OP_DROP) and a map-based nonce encoding inchannel_reestablishandrevoke_and_ackkeyed by funding TXID, laying the groundwork for splice support. The nonce type is now auto-detected from the negotiated channel type rather than peer feature bits, ensuring correct behavior across all recovery and resynchronization paths. Taproot channels must be requested explicitly withlncli openchannel --channel_type=taproot(the baretaprootstring now selects the production variant;taproot-stagingopens the legacy staging variant, andtaproot-finalis kept as a deprecated alias fortaproot), and must remain private until announced taproot channels are supported. The RPCCommitmentTypeenum gains aTAPROOTalias forSIMPLE_TAPROOT_FINALso new RPC clients can use the same short name. -
Added taproot channel support for RBF cooperative close. The new RBF-based cooperative close protocol (enabled with
--protocol.rbf-coop-close) now fully supports simple taproot channels. This includes MuSig2 partial signature handling with the JIT (just-in-time) nonce pattern, where closer nonces are bundled with signatures inClosingCompleteand closee nonces are rotated viaNextCloseeNonceinClosingSigfor each RBF iteration. The implementation prevents nonce reuse across RBF rounds by storing theMusigPartialSigin the protocol state machine and invalidating nonces after each signing round completes. -
Added rate limiting and a channel-presence gate for incoming onion messages. Two new byte-denominated token-bucket limiters run at ingress — one per peer, one global — so small onion messages pay proportionally less of the budget than spec-max ones. Defaults are
0.5 Mbps(512 Kbps, 256 KiB burst) per peer and5 Mbps(5120 Kbps, 1600 KiB burst) globally, tunable viaprotocol.onion-msg-peer-kbps,protocol.onion-msg-peer-burst-bytes,protocol.onion-msg-global-kbps, andprotocol.onion-msg-global-burst-bytes. Setting both the rate and the burst of a given limiter to0disables it; setting only one to0, or a burst smaller than a maximum-sized onion message, is rejected at startup. Incoming onion messages from peers with no fully open channel are also dropped at ingress as a Sybil-resistance layer; pending channels are excluded. Operators who want to accept onion messages from peers regardless of channel state can setprotocol.onion-msg-relay-all=trueto skip the channel-presence gate; the rate limiters still apply. See docs/onion_message_rate_limiting.md for the adversary model, the layers, the defaults, and operator recipes.
RPC Additions
-
Added
DeleteForwardingHistoryRPC to the router sub-server, allowing operators to selectively purge old forwarding events from the database. Deletion requires the target cutoff timestamp to be at least 1 hour in the past, preventing accidental removal of recent data. -
The
WaitingCloseChannelresponse inPendingChannelsnow includes two new fields via #10509:blocks_til_close_confirmed, showing the remaining confirmations until a closed channel is considered fully resolved, andclose_height, the block height at which the closing transaction was first confirmed. These build on the reorg-safe confirmation logic introduced in #10331, where the required number of confirmations scales with channel capacity. -
Added support for coordinator-based MuSig2 signing patterns with two new RPCs:
MuSig2RegisterCombinedNonceallows registering a pre-aggregated combined nonce for a session (useful when a coordinator aggregates all nonces externally), andMuSig2GetCombinedNonceretrieves the combined nonce after it becomes available. These methods provide an alternative to the standardMuSig2RegisterNoncesworkflow and are only supported in MuSig2 v1.0.0rc2. -
The
EstimateFeeRPC now supports explicit input selection. Users can specify a list of inputs to use as transaction inputs via the newinputsfield inEstimateFeeRequest.
lncli Additions
- The
estimatefeecommand now supports the--utxosflag to specify explicit inputs for fee estimation. - The
walletrpc.SignPsbtnow has a correspondinglncli wallet psbt signcommand, and can be used to sign a PSBT.
Improvements
Functional Updates
-
Allow multiple read-only RPC middleware interceptors to be registered simultaneously.
-
Added support for the
upfront-shutdown-addressconfiguration inlnd.conf, allowing users to specify an address for cooperative channel closures where funds will be sent. This applies to both funders and fundees, with the ability to override the value during channel opening or acceptance. -
Rename experimental endorsement signal to accountable to match the latest proposal.
RPC Updates
-
routerrpc HTLC event subscribers now receive specific failure details for invoice-level validation failures, avoiding ambiguous
UNKNOWNresults. #10520 -
A new
wallet_syncedfield has been added to theGetInfoRPC response. This field indicates whether the wallet is fully synced to the best chain, providing the wallet's internal sync state independently from the compositesynced_to_chainfield which also considers router and blockbeat dispatcher states. -
SubscribeChannelEvents now emits channel update events to be able to subscribe to state changes.
-
The
GetDebugInfoRPC request now accepts aninclude_logflag. By default, only the configuration map is returned. Wheninclude_logis set totrue, the log file content is also included in the response.
lncli Updates
-
The
getdebuginfocommand now supports an--include_logflag. By default, only the daemon's configuration is returned. When set, the log file content is also included in the response. -
The
encryptdebugpackagecommand now supports an--include_logflag. When set, the log file content is included in the encrypted debug package.
Breaking Changes
-
Increased MinCLTVDelta from 18 to 24 to provide a larger safety margin above the
DefaultFinalCltvRejectDelta(19 blocks). This affects users who create invoices with customcltv_expiry_deltavalues between 18-23, which will now require a minimum of 24. The default value of 80 blocks for invoice creation remains unchanged, so most users will not be affected. Existing invoices created before the upgrade will continue to work normally. -
The
GetDebugInfoRPC no longer returns log file content by default. Clients that rely on thelogfield must now explicitly setinclude_logtotruein the request. Thelncli getdebuginfoandlncli encryptdebugpackagecommands similarly require the--include_logflag to include logs in the output. -
Removed the deprecated payment RPCs and
outgoing_chan_idfield that were announced for removal in 0.21 via the 0.20 release notes. Callers must migrate to the V2 equivalents:Removed RPC Replacement lnrpc.SendPayment(streaming)routerrpc.SendPaymentV2lnrpc.SendPaymentSyncrouterrpc.SendPaymentV2lnrpc.SendToRoute(streaming)routerrpc.SendToRouteV2lnrpc.SendToRouteSyncrouterrpc.SendToRouteV2routerrpc.SendPayment(streaming)routerrpc.SendPaymentV2routerrpc.SendToRouterouterrpc.SendToRouteV2routerrpc.TrackPayment(streaming)routerrpc.TrackPaymentV2This also removes the corresponding REST routes
POST /v1/channels/transaction-stream,POST /v1/channels/transactions, andPOST /v1/channels/transactions/route. The orphanrouterrpc.SendToRouteResponsemessage (only used by the removedrouterrpc.SendToRouteRPC) is also dropped.In addition, the deprecated
outgoing_chan_idfield is removed fromlnrpc.QueryRoutesRequestandrouterrpc.SendPaymentRequest(proto tags 14 and 8 respectively, now reserved). Callers must use the multi-channeloutgoing_chan_idsfield introduced in 0.20. -
Removed the deprecated
--tor.v2configuration flag. Tor stopped serving v2 onion services in October 2021, and lnd no longer produces v2 on any code path;tor.OnionHostToFakeIPis also gone. Operator input is rejected at the boundary:--externalip,--listen,lncli connect, andlncli wtclient towers addfail fast on a v2.onionstring, so operators upgrading with a v2 entry inlnd.confmust remove it before lnd will start. Persisted state carried over from a previous version is also filtered before use: the self-node announcement strips any v2 entry from the source-node record before signing; the watchtower client drops v2 entries from each persisted tower's address list (skipping the tower entirely if no non-v2 address remains, so the operator can attach a fresh v3 address); the autopilot connector, graph bootstrapper, and static-channel backup restore paths skip v2 entries before attempting outbound dials. The Tor controller'sADD_ONIONpath is restricted to v3 keys, including the encrypted on-disk legacy-key fallback. The on-disk records themselves are left intact.Peer-signed announcements that still carry v2 are handled byte-for-byte: the
lnwireandgraph/dbcodecs round-trip v2 soDataToSignreproduces the signed bytes, signatures validate, and the announcement is persisted and re-broadcast unchanged. RPCs likeGetNodeInfoandDescribeGraphstill expose the full address set.
Performance Improvements
-
Let the channel graph cache be populated asynchronously on startup. While the cache is being populated, the graph is still available for queries, but all read queries will be served from the database until the cache is fully populated. This new behaviour can be opted out of via the new
--db.sync-graph-cache-loadoption. -
Autopilot's graph-wide channel scoring traversal no longer requests node addresses from the graph backend, since the scoring code does not consume them. This removes an unnecessary address batch-load on the SQL backend, and lets the kvdb backend serve the traversal from the in-memory graph cache when it is loaded.
-
Invoice pagination queries no longer use
OFFSET. The five invoice filter queries previously usedLIMIT+OFFSETfor internal batching, which requires the database to scan and discard all preceding rows on every page. All pagination is now cursor-based (WHERE id >= cursor), making every page an efficient primary-key range scan regardless of how deep into the result set the query is. -
Replace the catch-all
FilterInvoicesSQL query with five focused, index-friendly queries (FetchPendingInvoices,FilterInvoicesBySettleIndex,FilterInvoicesByAddIndex,FilterInvoicesForward,FilterInvoicesReverse). The old query usedcol >= $param OR $param IS NULLpredicates and aCASE-basedORDER BYthat prevented SQLite's query planner from using indexes, causing full table scans. Each new query carries only the parameters it actually needs and uses a directORDER BY, allowing the planner to perform efficient index range scans on the invoice table. -
Fix full table scans on the HTLC settlement hot path. Replace the catch-all
GetInvoicequery (which usedOR $1 IS NULLpredicates that forced full table scans) with three dedicated queries targeting uniquely-constrained columns. Also drop four redundant indexes that duplicated UNIQUE constraints or were never used as query filters. -
Optimize the v1 node horizon query. Split the
GetNodesByLastUpdateRangequery into separate all-nodes and public-only variants, removing a dynamicCOALESCE/ORbranch that defeated the query planner. The public-onlyEXISTScheck is rewritten as two direct index probes instead ofnode_id_1 OR node_id_2. Supporting indexes are upgraded to composite keys matching the full query shapes. On SQLite, the hot public-only path sees a ~42% speedup; on the previous code it could stall for minutes. -
Tombstone closed channels on KV-over-SQL backends. Closing a long-lived channel previously issued a single
DeleteNestedBucketinside the close transaction. On the kvdb-on-SQL schema (sqlite, postgres) that delete fans out into a row-by-rowON DELETE CASCADEover the channel's revocation log and forwarding-package bucket, holding the database write-lock for many seconds — long enough on channels with millions of states to stall HTLC forwarding, time out htlcswitch retries, and trigger force-close cycles.CloseChannelnow skips the cascading delete on these backends; the outpoint-index flip fromoutpointOpentooutpointClosed(already performed by the existing close path) is the authoritative closed-channel marker, and every reader of the open-channel bucket consults it before treating a channel as open. The bulk historical state — the chanBucket itself, the revocation log, and the per-channel forwarding-package bucket — remains on disk for the channel's lifetime in this database and is reclaimed wholesale by the upcoming native-SQL channel-state migration. bbolt and etcd retain the synchronous one-shot close path, where nested-bucket deletion is already cheap.⚠️ Downgrade warning. On sqlite/postgres, once a channel is closed under this build the chanBucket and its nested state remain on disk; the close is signalled only by the
outpointClosedflip in the outpoint index. Earlierlndreleases do not consult that flip when iteratingopenChannelBucket, so downgrading to a pre-0.21 binary after closing channels on these backends will resurrect those channels as open inlistchannels,pendingchannels, and the chain-watch path. Operators who close channels on sqlite/postgres after upgrading should treat the upgrade as one-way for that database; bbolt and etcd users are unaffected because the close path on those backends still deletes the chanBucket.
Deprecations
⚠️ Warning: Deprecated fields in lnrpc.Hop will be removed in release version 0.22
The following deprecated fields in the lnrpc.Hop
message will be removed:
| Field | Deprecated Since | Replacement |
|---|---|---|
chan_capacity |
0.7.1 | None |
amt_to_forward |
0.7.1 | amt_to_forward_msat |
fee |
0.7.1 | fee_msat |
⚠️ Warning: The deprecated fee rate option --sat_per_byte will be removed in release version 0.22
The deprecated --sat_per_byte option will be fully removed. This flag was
originally deprecated and hidden from the lncli commands in v0.13.0
(PR#4704). Users should
migrate to the --sat_per_vbyte option, which correctly represents fee rates
in terms of virtual bytes (vbytes).
Internally --sat_per_byte was treated as sat/vbyte, this meant the option
name was misleading and could result in unintended fee calculations. To avoid
further confusion and to align with ecosystem terminology, the option will be
removed.
The following RPCs will be impacted:
| RPC Method | Messages | Removed Option |
|---|---|---|
lnrpc.CloseChannel |
lnrpc.CloseChannelRequest |
sat_per_byte |
lnrpc.OpenChannelSync |
lnrpc.OpenChannelRequest |
sat_per_byte |
lnrpc.OpenChannel |
lnrpc.OpenChannelRequest |
sat_per_byte |
lnrpc.SendCoins |
lnrpc.SendCoinsRequest |
sat_per_byte |
lnrpc.SendMany |
lnrpc.SendManyRequest |
sat_per_byte |
walletrpc.BumpFee |
walletrpc.BumpFeeRequest |
sat_per_byte |
Technical and Architectural Updates
BOLT Spec Updates
Testing
-
Added unit tests for TLV length validation across multiple packages. New tests ensure that fixed-size TLV decoders reject malformed records with invalid lengths, including roundtrip tests for Fee, Musig2Nonce, ShortChannelID and Vertex records.
-
Added a bitcoind-backed miner backend to
lntest. Integration tests can now select the miner backend independently from the chain backend, and CI now covers thebackend=bitcoind minerbackend=bitcoindpath.
Database
-
Prevent silent data corruption when reusing the same database across different Bitcoin networks. On first startup the active network is persisted in a new
chain_paramstable; on every subsequent restart lnd compares the stored value against the configured network and refuses to start if they differ, printing a clear error message with remediation steps. This safeguard applies to both the PostgreSQL and SQLite native-SQL backends when running with--db.use-native-sql. -
Freeze the graph SQL migration code to prevent the need for maintenance as the sqlc code evolves.
-
Prepare the graph DB for handling gossip V2 nodes and channels 1 2 3 4, 5, 6.
-
Version the graph horizon queries (
NodeUpdatesInHorizon,ChanUpdatesInHorizon) to support both v1 (time-based) and v2 (block-height-based) gossip ranges. The v1 end-time bound is corrected from inclusive to exclusive to match the BOLT 07gossip_timestamp_filterspec. New SQL queries and composite indexes are added for efficient v2 block-height range scans. -
Version
FilterKnownChanIDsand fixFetchChannelEdgesByIDzombie fallback so that gossip channel filtering and zombie edge lookups use the correct gossip version instead of hardcoding v1. -
Updated waiting proof persistence for gossip upgrades by introducing typed waiting proof keys and payloads, with a DB migration to rewrite legacy waiting proof records to the new key/value format (#10633).
-
Payment Store SQL implementation and migration project:
- Introduce an abstract payment store interface and refacotor the payment related LND code to make it more modular.
- Implement the SQL backend for the payments database
- Implement query methods (QueryPayments,FetchPayment) for the payments db SQL Backend
- Implement insert methods for the payments db SQL Backend
- Implement third(final) Part of SQL backend payment functions
- Finalize SQL payments implementation enabling unit and itests for SQL backend
- Thread context through payment db functions Part 1
- Thread context through payment db functions Part 2
- Finalize SQL implementation for payments db
- Add the KV-to-SQL payment
migration with
comprehensive tests. The migration is currently dev-only, compiled behind
the
test_db_postgres,test_db_sqlite, ortest_native_sqlbuild tags. - Various SQL payment store
improvements:
optimize schema indexes, improve query performance for payment filtering
and failed attempt cleanup, fix cross-database timestamp handling, add
omit_hopsoption toListPaymentsto reduce response size, and increase the default SQLite cache size. - The SQL payments migration is promoted to production
code. Previously the
migration was hidden behind the
test_native_sqlbuild tag; it is now compiled into mainline builds and available to all users who have thenative-sqlsetting enabled.
Code Health
-
Update taproot detection to accommodate buried activation (and modified RPC
getdeploymentinforesponse) beginning in Bitcoin Core v32. -
Migrated gossip result handling from
chan errortoactor.Future[error]. The three buffered-channel patterns in the discovery package are replaced with idempotent promises, eliminating a class of latent deadlock bugs when a deferred message copy was re-enqueued and processed a second time. A newlnutils.ContextFromQuithelper bridges the existingquitchannels tocontext.Context, so all gossip awaits now respect shutdown uniformly.
Robustness
- Drop onion messages that would cycle back to the sending peer. When the resolved next hop of an incoming onion message is the same peer that delivered it, the message is now dropped instead of being forwarded back over the connection it arrived on. This closes a trivial traffic-amplification vector and covers both the direct next-node-ID and SCID-resolved paths.
Tooling and Documentation
-
Added missing
lncli:tags forSendPaymentV2,SendToRouteV2, andEstimateRouteFeein therouterrpcproto definitions so that the generated API documentation correctly links to their correspondinglnclicommands (sendpayment,sendtoroute,estimateroutefee). -
Overhauled Docker documentation and environment to modernize the developer onboarding flow. Key updates include migrating to Docker Compose V2, updating base images (btcd v0.25.0, Go 1.25.5), and transitioning the documentation to focus on a more reliable "Simnet" workflow while removing obsolete faucet references.
-
Android Lndmobile 16 KB page size for native libraries The Android
Lndmobile.aarbuild now passes-Wl,-z,max-page-size=16384to the linker, keeping the generated native library compatible with newer Android devices that use 16 KB memory pages while preserving compatibility with existing 4 KB page-size devices.
Contributors (Alphabetical Order)
- Abdulkbk
- AbelLykens
- ajaysehwal
- Andras Banki-Horvath
- bitromortac
- Boris Nagaev
- Calvin Zachman
- Dario Anongba
- Elle Mouton
- elnosh
- Erick Cestari
- Euler-B
- ffranr
- George Tsagkarelis
- Gijs van Dam
- hieblmi
- Liongrass
- Matt Morehouse
- Matthew Zipkin
- Mohamed Awnallah
- Nishant Bansal
- Olaoluwa Osuntokun
- Oliver Gugger
- Pins
- Suheb
- ViktorT-11
- Yash Bhutwala
- Yong Yu
- Ziggie