Commit graph

326 commits

Author SHA1 Message Date
Olaoluwa Osuntokun
e5e134ddac peer+lnwallet/chancloser: advance the legacy closer from one goroutine
In this commit, we give the legacy ChanCloser a single owner, rather than
letting two goroutines advance it. The peer's channelManager drives the state
machine for the Shutdown and ClosingSigned messages that come off the wire, and
for local close requests. The link drives it as well: while we wait for the
channel to drain we register a flush hook, and the link invokes that hook from
its own goroutine, where it called BeginNegotiation directly. Nothing kept the
two apart, so the state field, the priorFeeOffers map, and the signing step
could all be touched at once. Under `go test -race` this shows up as a data race
on the state field.

Rather than reach for a lock, we route the flush through the channelManager. The
hook now only reports the channel ID over a new chanCloseFlushed channel, and
handleChanFlushed picks it up next to the close messages. Every transition, the
cached offer processing, the fee map, and the signing then happen on the one
goroutine, so the closer needs no synchronization of its own. We spell that out
on the type, since it's an invariant a new caller can break from the outside.

The report goes out from a fresh goroutine, which matters more than it looks.
The link may well be holding its own lock while it invokes the hook, and
channelManager reaches for that same lock in DisableAdds, so blocking on the
handoff would trade the race for a deadlock. The `go` in front of RemoveLink
just above it is there for the same reason.

We look the closer up with a plain map load rather than through
fetchActiveChanCloser, as that one builds a fresh closer when it doesn't find
an existing one, and a flush that lands after the negotiation was torn down has
no business starting a new negotiation.

One behavior change falls out of the move: the flush path now runs the same
finalization tail as the message path. It skipped that before, so a responder
that drained a cached offer would reach closeFinished and broadcast, but nothing
ran finalizeChanClosure until the next close message showed up, and having
already sent its final signature, there may not be one. The link == nil path
already ran the tail, so this makes all three paths agree.

The new test drives a close with a link that hands us the flush hook instead of
running it inline, so we can check that negotiation waits on the report, and
that a report for a channel we have no closer for is dropped.
2026-08-05 12:22:56 -07:00
Jared Tobin
ceff3ceb4b
peer: never use RBF coop close for aux channels
The RBF coop close flow was selected purely from the peer-level
feature bits (rbfCoopCloseAllowed), with no per-channel exclusion.
The RBF close state machine does not invoke any of the aux closer
hooks: the Shutdown message it sends carries no aux custom records,
and the close transaction it negotiates contains no aux outputs. For
a taproot asset (overlay) channel this means the funding output --
which anchors the asset commitment -- is spent by a transaction that
does not re-commit the assets, irrevocably destroying them on-chain.
The aux closer then fails to finalize the confirmed close (it was
never asked to produce vPackets), which blocks the chain watcher's
coop close handler and leaves the channel stuck in waiting-close.

See lightninglabs/taproot-assets#2196 for an instance of this
happening in the wild.

Extend rbfCoopCloseAllowed to take the channel type: it now requires
the RBF feature bits AND that the channel type carries no tapscript
root, and is used at every site that chooses between the RBF closer
and the legacy negotiate closer. The RBF close actor's own eligibility
check is dropped entirely: an actor is only ever registered after
initRbfChanCloser has vetted the channel, so the check was redundant.
Aux channels now always fall back to the legacy closer, which is
aux-aware, regardless of the negotiated feature bits. Since no RBF
msg-router endpoint is registered for aux channels, an incoming Shutdown
from the peer likewise falls through to the legacy close handling. As a
backstop, initRbfChanCloser now refuses to construct an RBF closer for
aux channels outright.
2026-07-14 13:24:33 -02:30
ziggie
a459bc4672
peer: use channel state open channel
Update peer channel loading, validation, and test helpers to use
chanstate.OpenChannel directly.

The peer package still depends on channeldb for store-level errors and
helpers, but no longer needs the OpenChannel alias in its public
channel-state boundary.
2026-07-07 07:18:59 -03:00
ziggie
68ad283d3e
chanstate: remove store generics
Drop the temporary channel type parameter from the channel-state store
interfaces now that OpenChannel lives in chanstate.

The domain store subinterfaces now refer to *OpenChannel directly while
retaining the same backend-independent shape. Update callers and
compatibility aliases to use the concrete Store and ChannelShell types.
2026-07-06 15:51:20 -03:00
ziggie
0229e0ada3
channeldb: derive channel packagers
Remove the KV forwarding packager from OpenChannel and derive a
ChannelPackager inside the channeldb store methods that need one.

This keeps the backend-specific kvdb transaction helper in channeldb,
so the OpenChannel type no longer carries that dependency toward
chanstate.
2026-07-06 15:50:53 -03:00
ziggie
f53d615c1b
chanstate: make store channel types generic
Move the small value types referenced by chanstate.Store out of
channeldb. This includes ChannelConfig, ChannelStatus,
ChannelCloseSummary, ChannelShell, ChanCount, and FinalHtlcInfo. Leave
aliases in channeldb so existing callers keep compiling while the
backend still lives there.

Parameterize the Store subinterfaces over the channel type and instantiate
current callers with *channeldb.OpenChannel. This removes the chanstate
-> channeldb import edge without moving OpenChannel yet, keeping the
first step reviewable and backend-neutral.
2026-07-06 15:50:41 -03:00
Oli
8047149c6a
multi: upgrade to btcd v2 modules
Migrate all btcd dependencies to the new per-package v2 modules (wire/v2,
txscript/v2, chaincfg/v2, chainhash/v2, btcutil/v2, psbt/v2, btcec/v2)
introduced by btcd v0.26.0, and pin the tagged ecosystem versions:
btcwallet v0.17.0, neutrino v0.18.0 and lightning-onion v1.4.0.

The bulk of the import rewrite was produced by the scripted diff from
https://github.com/btcsuite/btcd/pull/2547 (followed by 'make rpc'). The
address symbols that moved out of btcutil into the new address package
are imported as btcaddr where a local "address" variable would otherwise
shadow them. The go.mod/go.sum updates and the remaining manual
compilation fixes are folded into this single commit so it builds on its
own (the migration was previously split into a reproducible scripted-diff
plus follow-ups, intended to be squashed on merge).
2026-06-24 10:58:36 -07:00
Erick Cestari
aca27e27f5
lint: remove redundant loop var copies in test files
Since Go 1.22 loop variables are scoped per-iteration, so the
`x := x` / `a, b := a, b` copies inside range/for loops are no longer
needed. This removes the existing redundant copies in test files.
2026-06-03 15:32:45 -03:00
Jared Tobin
c5f3ad33f4
chancloser: remove dead ChannelFlushed.FreshFlush field
FreshFlush is never read in any transition handler. The only
producer (peer/brontide.go) sets it unconditionally to true,
and after the previous commit removed expectChanPendingClose,
the test loops that iterated over {true, false} no longer
differentiate between the two values.

Remove the field, the unconditional assignment, and collapse
the test loops into single sub-tests.
2026-05-14 10:21:29 -02:30
Jared Tobin
711a4a400d
chancloser: remove MarkCoopBroadcasted(nil) calls
Remove the two call sites that set ChanStatusCoopBroadcasted
before a cooperative close transaction exists:

 - BeginNegotiation in the legacy close path (chancloser.go)
 - ChannelFlushed handling in the RBF close path
   (rbf_coop_transitions.go)

Both calls passed nil as the close tx, creating a "limbo" state
where ChanStatusCoopBroadcasted is set but no close transaction
is stored. This is unnecessary because ShutdownInfo — persisted
earlier by MarkShutdownSent in initChanShutdown / the RBF
ShutdownPending transition — already serves as the durable
signal that the shutdown flow was entered.

ChanStatusCoopBroadcasted should only be set when a real close
transaction exists, which this change preserves.
2026-05-14 10:20:29 -02:30
ziggie
d08b1b07c7
peer: depend on chanstate Store
Replace the peer config's concrete channel state DB dependency with
chanstate.Store. Brontide only needs channel lookups, closed-channel
lookup, and initial forwarding policy access from the channel-state
store.
2026-05-11 18:54:30 -03:00
Olaoluwa Osuntokun
fa2d0f9904 peer: register the rbfCloseActor, have RPC route fee bumps to it
In this commit, we now register the rbfCloseActor when we create the rbf
chan closer state machine. Now the RPC server no longer neesd to
traverse a series of maps and pointers (rpcServer -> server -> peer ->
activeCloseMap -> rbf chan closer) to trigger a new fee bump.

Instead, it just creates the service key that it knows that the closer
can be reached at, and sends a message to it using the returned
actorRef/router. We also hide additional details re the various methods
in play, as we only care about the type of message we expect to send and
receive.
2026-04-22 17:33:04 -07:00
Olaoluwa Osuntokun
2a3ae1efe8 peer: create new rbfCloseActor to decouple RPC RBF close bumps
In this commit, we create a new rbfCloseActor wrapper struct. This will
wrap the RPC operations to trigger a new RBF close bump within a new
actor. In the next commit, we'll now register this actor, and clean up
the call graph from the rpc server to this actor.
2026-04-22 16:46:55 -07:00
Olaoluwa Osuntokun
335b75981f lncfg+peer+server: add protocol.onion-msg-relay-all to bypass channel gate
Add a new protocol option, protocol.onion-msg-relay-all, that controls
whether incoming onion messages are required to come from peers with a
fully open channel. The default is false, which preserves the existing
behavior: the channel-presence gate drops messages from peers with no
channel before the rate limiters are consulted, so a new no-cost
identity cannot burn any per-peer byte budget and saturate the global
bucket. Setting the flag to true skips the gate so that onion messages
from any peer are admitted into the per-peer + global IngressLimiter
pipeline.

The flag is plumbed through ProtocolOptions in both the default and
integration build variants of lncfg/protocol*.go, threaded into the
peer subsystem as peer.Config.OnionRelayAll, and wired by the server
from s.cfg.ProtocolOptions.OnionMsgRelayAll alongside the existing
OnionLimiter field. allowOnionMessage gains a relayAll bool parameter;
the gate check becomes "if \!relayAll && \!hasChannel { drop }" so the
semantics of hasChannel stay pure — it still means "this peer has a
channel" — and the policy toggle lives entirely in the caller's
configuration rather than being spread across gate-state and flag
state.

sample-lnd.conf gains a commented-out entry for the new option with
the default value and an operator-facing note that enabling it trades
the Sybil-resistance property of the gate for reachability to peers
with whom we have no channel.

A new TestAllowOnionMessageRelayAll unit test exercises the four
(hasChannel, relayAll) combinations at the helper level, including
the key new behavior — a peer with hasChannel=false being rejected
under relayAll=false and admitted into the limiter under
relayAll=true — and the nil-limiter path under relayAll=true, which
must still accept. The existing allowOnionMessage tests were
extended with the new parameter set to false so they continue to
assert the gate semantics unchanged.
2026-04-15 13:23:50 -07:00
Olaoluwa Osuntokun
c0827e8e39 peer: gate onion message ingress on having an open channel
Onion message forwarding is an unpaid side channel. Without any peer
qualification the byte-bucket limiters added in the previous commits are
our only defense against a Sybil attacker: an attacker that can cheaply
spin up N identities and burn a full per-peer byte budget on each one
saturates the global bucket and converts the aggregate cap into a
service-denial primitive against legitimate channel peers. This was
raised on PR review — the per-peer cap is good, but the global cap on
its own is a Sybil multiplier if peer identity is free. The proper fix
is to make new identities cost real capital, which is what requiring a
funded channel does.

This commit adds a channel-presence gate as the first check in
allowOnionMessage, ahead of both the per-peer and the global rate
limiters. Messages from peers that do not have at least one fully
open channel with us are dropped with a new dropReasonNoChannel
sentinel and never allocate any rate limiter state — the gate runs
before either limiter is consulted, so no-channel peers cannot burn
tokens on any bucket. Pending channels are deliberately excluded from
the check: they are represented as nil values in the activeChannels
map, are cheap to open and prone to getting stuck, and so do not
provide the capital-cost guarantee the Sybil defense depends on.
Existing Brontide cleanup paths (StopOnionActorIfExists,
OnionPeerLimiter.Forget) already handle teardown on peer disconnect;
nothing new is needed there because the gate keeps no-channel peers
from ever allocating per-peer state in the first place.

For the hot path we cannot afford to iterate the activeChannels
registry on every incoming onion message, so Brontide now carries a
numActiveChans atomic.Int32 that shadows the count of non-pending
entries in activeChannels. hasActiveChannels is a single atomic Load
and is therefore O(1). The counter is maintained in lockstep with
activeChannels at every mutation site: loadActiveChannels increments
it as it populates the registry during Start(); addActiveChannel uses
a new lnutils.SyncMap.Swap method (a thin typed wrapper around
sync.Map.Swap) to atomically replace any prior entry so that both
brand-new channels and pending-to-active promotions bump the counter
by exactly one; WipeChannel and handleRemovePendingChannel both use
LoadAndDelete so they can inspect the prior value and only decrement
when the removed entry was non-nil. Under race, this keeps the
counter and the map consistent even when RPC WipeChannel races with
the channelManager goroutine.

The accompanying unit tests cover: the no-channel drop path at the
allowOnionMessage level, asserting that neither the global stub
counter nor the per-peer limiter's dropped counter move when the
gate fires; the subsequent channel-gained path on the same peer,
asserting the same message is accepted once hasChannel flips; and a
focused Brontide-level test that walks the counter through initial
emptiness, a pending-only state (counter must stay at zero), a
pending-to-active promotion via direct Store + Add, the pending
delete path through handleRemovePendingChannel (must not underflow),
and the active delete path through LoadAndDelete + Add(-1) that
WipeChannel uses internally. Running with -race confirms the
Swap/LoadAndDelete patterns keep the counter and the map in sync
under concurrent access.
2026-04-15 13:23:50 -07:00
Olaoluwa Osuntokun
9cad57bfce peer: enforce onion message rate limits at ingress
This commit plumbs the combined IngressLimiter (per-peer + global)
through peer.Config and consults it from the readHandler's
*lnwire.OnionMessage case. The decision is factored into a small
allowOnionMessage helper so that the ingress policy is directly
unit-testable without standing up a full Brontide harness. Per-peer is
checked first inside the IngressLimiter: if we consulted the global
limiter first, a peer whose own bucket was already empty would still
get to burn a global token on each attempt, letting a single hostile
peer drain the shared budget and starve legitimate peers.

peer.Config carries a single OnionLimiter field of IngressLimiter type;
the brontide readHandler calls a single AllowN per incoming onion
message and dispatches on sentinel errors via errors.Is for the
first-drop log path. Nil limiter values are treated as "disabled"
throughout, which both preserves the pre-change behavior when onion
messaging is entirely turned off and keeps the brontide test harness
from needing to construct real limiters. Per-peer bucket state is
retained across disconnect at the IngressLimiter layer so a peer
cannot cycle the connection to reset its per-peer allowance.

OnionMessage also gains a WireSize method that computes the
on-the-wire size directly from the in-memory fields (no round-trip
through Encode) so the hot ingress path can charge the right number of
byte tokens without paying for a full serialization.

The accompanying unit tests cover the nil/disabled path, the
per-peer-rejects-first ordering invariant (asserting the global
limiter is not consulted when the per-peer bucket is empty), the
global rejection path, per-peer isolation across distinct pubkeys, and
a small concurrent stress test that asserts every attempt is accounted
for as either accepted or dropped and that the total accepted count
equals the configured burst under -race. A property-based rapid test
on WireSize guards against silent divergence from WriteMessage if the
OnionMessage wire format ever gains a TLV extension.
2026-04-15 13:23:50 -07:00
Olaoluwa Osuntokun
7a18fba663 watchtower: add production taproot channel support to justice kit
Wire channel type through BreachRetribution and the watchtower blob
system to support production taproot channels with final scripts.

The key changes are:

1. Add ChanType field to BreachRetribution so downstream consumers
   (including the watchtower) can determine the script variant.

2. Add FlagTaprootFinalChannel blob type flag and
   TypeAltruistTaprootFinalCommit blob type to distinguish production
   from staging taproot channels in watchtower backups.

3. Add TaprootFinalCommitment to the watchtower's CommitmentType enum
   with appropriate witness type and size mappings.

4. Update taprootJusticeKit to use WithProdScripts() when constructing
   script trees for production taproot channels. The isFinal flag is
   set during construction from BreachRetribution.ChanType and during
   deserialization from the blob's commitment type.

Without this change, the watchtower would construct justice transactions
using staging scripts for production taproot channels, resulting in
invalid witnesses that fail to sweep breached outputs.
2026-04-13 12:21:42 -07:00
Olaoluwa Osuntokun
08c42b19da multi: add custom nonce rand support to MuSig2 sessions
In this commit, we add the ability to inject a custom random source
for generating JIT (Just-In-Time) signing nonces in MuSig2 sessions.
By default, MuSig2 signing nonces are generated using crypto/rand,
which makes signatures non-deterministic across runs. For test vector
generation, we need fully reproducible signatures from a fixed seed.

A new `customNonceRand` field is threaded through `MusigSession`,
`MusigSessionCfg`, `MusigPairSession`, and exposed via the
`WithCustomSigningRand` channel option. When set, the custom reader
is passed to `musig2.WithCustomRand()` during JIT nonce generation
in `SignCommit`. All existing callers pass `fn.None[io.Reader]()` to
preserve the current behavior of using the system CSPRNG.
2026-04-13 12:21:42 -07:00
Olaoluwa Osuntokun
29de2c8618 cmd/commands: add taproot-final to lncli open command 2026-04-13 12:21:42 -07:00
Olaoluwa Osuntokun
ac71ea7559 discovery+funding+peer+server: migrate gossip result to actor.Future[error]
In this commit, we eliminate the three buffered chan error patterns in
the discovery package and replace them with actor.Promise[error]/
actor.Future[error]. The old pattern is error-prone: if a channel is
completed more than once (e.g. when a deferred message copy is
re-enqueued and processed again), the second write to a capacity-1
channel blocks forever. actor.Promise.Complete() is idempotent via
sync.Once, so the second call is always a safe no-op regardless of
whether anyone holds a reference to the Future.

Additionally, PropagateChanPolicyUpdate previously blocked on <-errChan
after enqueuing a policy update with no quit-channel check, creating a
latent deadlock if the gossiper shut down between enqueue and send. It
now uses AwaitGossipResult with a ContextFromQuit-derived context, so
shutdown is always respected.

This is an atomic migration that updates all callers in the same
commit so each commit builds standalone. The three main pieces are:

discovery

networkMsg.err chan error becomes errPromise actor.Promise[error].
chanPolicyUpdateRequest.errChan chan error becomes errPromise.
syncTransitionReq.errChan chan error becomes errPromise. All ~65 sites
that previously wrote to the error channel now call
completeGossipResult(nMsg.errPromise, err) instead.

ProcessRemoteAnnouncement and ProcessLocalAnnouncement now return
actor.Future[error] instead of chan error. The capacity-2 buffer
comment on ProcessRemoteAnnouncement, which was itself a workaround
for the old pattern, is removed along with the TODO referencing the
actor model redesign. ProcessSyncTransition in syncer.go follows the
same pattern: the errChan select is replaced with AwaitGossipResult
using a ContextFromQuit-derived context.

funding

The SendAnnouncement function type in funding.Config changes from
returning chan error to returning actor.Future[error]. The call sites
in addToGraph and announceChannel are updated to await the future with
AwaitGossipResult, passing a context derived from f.quit via
ContextFromQuit. Shutdown signals (context.Canceled and
discovery.ErrGossiperShuttingDown) are both mapped to
ErrFundingManagerShuttingDown via the new mapGossipError helper, which
also factors out the duplicated graph-rejected / unknown-error
handling. The three mock SendAnnouncement implementations in
manager_test.go are updated accordingly.

peer+server

In peer/brontide.go, the ProcessRemoteAnnouncement call in the gossip
stream handler intentionally discards the result since remote gossip
messages are fire-and-forget from the peer's perspective. The old
comment explaining why the chan error was unsafe to use is replaced
with a note that an unawaited Future[error] carries no overhead.

In server.go (applyChannelUpdate), the previous select on errChan and
the quit channel is replaced with ContextFromQuit + AwaitGossipResult.
2026-04-10 19:16:49 -07:00
Gijs van Dam
e87f4bfb6f onionmessage: use BackpressureMailbox for onion peer actors
This commit adds per-peer backpressure control to the onion message
actor system by introducing a BackpressureMailbox that uses Random
Early Detection (RED) to probabilistically drop messages when the
per-peer queue depth exceeds a configurable threshold.

The OnionActorFactory type now accepts variadic ActorOptions, allowing
the spawn call site (brontide) to provide per-peer mailbox configuration.
A DefaultOnionActorOpts helper provides the standard RED thresholds so
callers don't need to wire up the BackpressureMailbox manually.

Key changes:
- OnionActorFactory signature extended with ...ActorOption[*Request,
  *Response] so backpressure policy is no longer baked into the factory.
- NewOnionActorFactory drops its shouldDrop parameter; it forwards opts
  through to serviceKey.Spawn.
- DefaultOnionActorOpts(shouldDrop) returns the default backpressure
  options (BackpressureMailbox + DefaultOnionMailboxSize).
- peer.Config gains OnionActorOpts callback for per-peer customization.
- server.go creates default opts once and returns them for every peer.
2026-03-28 12:42:53 +01:00
Olaoluwa Osuntokun
f297c4782e
Merge pull request #10063 from lightningnetwork/taproot-rbf
multi: add taproot support to the new RBF close flow
2026-03-27 16:17:44 -07:00
Olaoluwa Osuntokun
d9284abebc lnwallet/chancloser: address lint and PR review feedback
Fix all lint issues across the taproot RBF coop close changes:

- Fix line length violations (ll) by wrapping long lines and adding
  nolint:ll where wrapping would hurt readability.
- Fix nlreturn: add blank lines before return statements.
- Fix misspell: correct "siganture" typos.
- Fix forcetypeassert: add checked type assertions.
- Fix nonamedreturns: remove named returns from function signatures.
- Fix usetesting: replace context.Background() with t.Context().
- Fix unused: remove unused remoteSchnorrSig variable.
- Fix whitespace: add newlines after multi-line func signatures.
- Fix gocritic appendAssign warning.
- Fix gci: correct import ordering.

Also address PR review comments from @erickcestari:

- Remove nonce cache in ClosingNonce() to prevent future footguns.
- Rename extractSigAndNonce to extractSigAndNonceFromClosingSig.
- Rename extractSigAndNonceFromComplete to
  extractSigAndNonceFromClosingComplete with channel type validation.
- Replace env.RemoteMusigSession \!= nil with env.IsTaproot().
- Swap manual mocks to mock.Mock in musig_nonce_order_test.go.
2026-03-27 14:04:25 -07:00
Olaoluwa Osuntokun
f4fff1726b lnwallet/chancloser: fix MuSig2 nonce reuse across RBF rounds
MusigChanCloser.ClosingNonce() cached the local nonce and returned
the same one on subsequent calls. Since each RBF round creates a new
MuSig2 session via ProposalClosingOpts() but passes the same SecNonce,
the btcd library's per-session nonce reuse guard was bypassed (fresh
Session each round). Signing different closing transactions (different
fees/sighashes) with the same secret nonce enables private key
extraction via simple linear algebra on the partial signatures.

Fix by adding ClearNonce() to the MusigSession interface and calling
it after each signing round completes, forcing fresh nonce generation
on every RBF iteration. Also fix a rebase issue where
updateAndValidateCloseTerms was not extracting NextCloseeNonce from
ClosingSig messages for subsequent RBF rounds.

lnwallet/chancloser: fix MuSig2 nonce reuse across RBF rounds

MusigChanCloser.ClosingNonce() cached the secret nonce and returned
the same one across RBF rounds. Since each round creates a new
MuSig2 session via ProposalClosingOpts() but passes the same
SecNonce, signing different closing transactions with different
sighashes enables private key extraction.

Fix this by:

1. Storing the full MusigPartialSig from LocalCloseStart in the
   LocalOfferSent state, eliminating the second CreateCloseProposal
   call in prepareClosingSignatures. This was also flagged in PR
   review as wasteful.

2. Adding InvalidateNonce() to the MusigSession interface. After
   the closer round completes (CompleteCooperativeClose), the closer
   nonce is invalidated so the next RBF round generates fresh. For
   the closee, the nonce is invalidated before generating the next
   closee nonce in createClosingSigMessage.
2026-03-27 14:04:25 -07:00
Olaoluwa Osuntokun
34a86ca800 multi: fix nonce handling bug
In this commit, we fix a nonce handling bug. The bug was unnoticed until
interop testing due to some inadvertent mutation. Before this commit, in
peer/brontide.go, we used the _same_ instance of the musig2 chan closer,
which masked the bug.

The issue was that we would attempt to generate a siganture for the
remote party _before_ we had applied their JIT nonce to our remote (used
to sign their close txn) musig session.

We first created a new test to confirm the issue (in peer, as it needed
to be in order to avoid a circular dep test). Without these changes, the
test fails.

The fix is two fold:
 1. Create two independent musig2 chan closers.
 2. Update the ordering to apply their nonce before we generate a
signature.
2026-03-27 14:04:25 -07:00
Olaoluwa Osuntokun
85adad753b multi: wire taproot RBF support throughout the stack
In this commit we, integrate the taproot RBF cooperative close
functionality throughout the LND stack. This includes updating
protocol configuration and peer connection handling to support
the new taproot closing flow.

The changes wire through the taproot channel detection, nonce
exchange during shutdown, and proper handling of the enhanced
wire protocol messages in the peer layer. This completes the
integration of taproot RBF cooperative close functionality,
providing a complete alternate closing path for taproot channels
that leverages MuSig2 signatures and implements proper nonce
rotation for secure RBF scenarios.
2026-03-27 14:04:25 -07:00
yyforyongyu
73770dbf07
peer: include ping pong-size in debug summaries
Expose num_pong_bytes in the ping message summary so ignored no-reply
pings are visible in debug logs. Add a focused test covering the summary
output for the sentinel range.
2026-03-26 18:55:13 +08:00
yyforyongyu
08b26b6137
lnwire+peer: ignore no-reply pings
Allow pings in the BOLT 1 no-reply range to decode and be ignored
instead of disconnecting peers. This keeps reconnects compatible with
peers that pad channel_reestablish with no-reply pings.
2026-03-26 18:55:13 +08:00
Elle Mouton
d4089661a8
peer: add mock BestBlockView to test peer config
The test Config in createTestPeer left BestBlockView nil. When the
PingManager's timer fires during a test, it calls
BestBlockView.BestBlockHeader() which panics on the nil receiver.
This was a flaky failure since it depended on a race between the
timer and test completion.

Add a trivial mockBestBlockView that returns an empty block header
and wire it into the test Config.
2026-03-24 11:49:48 +02:00
Elle Mouton
9a2c4c67c4
peer: fix nil deref in newPingPayload on BestBlockHeader error
The condition guarding the early return used && when it should have
used ||. When BestBlockHeader returns an error with a nil header, the
old code only short-circuited if the nil header equalled
lastBlockHeader. Otherwise it fell through to header.Serialize(),
causing a nil pointer dereference panic.

Change the condition to return the cached serialized header whenever
there is an error OR when the header is unchanged.
2026-03-24 11:49:35 +02:00
Gijs van Dam
e8074935d9
chore: fix linter issues in brontide.go
Post merge of #10089, a linter issues was introduced in `brontide.go`.
This commit fixes that issue.
2026-03-09 10:41:14 +01:00
Olaoluwa Osuntokun
392d4c8cb3
Merge pull request #10089 from gijswijs/onion-messaging-1
Onion message forwarding
2026-03-06 11:46:38 -06:00
George Tsagkarelis
8d30e7d160
peer: add test for createHtlcValidator 2026-03-04 20:19:43 +01:00
George Tsagkarelis
b1701cf232
peer: set and use aux htlc validator
When instantiating the lightning channel we now pass in the created HTLC
validator. This validator simply performs a bandwidth check and errors
out if that is insufficient.
2026-03-04 20:02:07 +01:00
Gijs van Dam
ba27627a70 multi: actor-based onion message forwarding
Add onion message forwarding capability using the OnionPeerActor for
communication. Messages are routed through a receptionist pattern where
each peer has a dedicated OnionPeerActor for handling message sends.

The OnionEndpoint uses the sphinx router for decoding and decrypting the
onion message packet and the encrypted recipient data in the payload of
the onion messages.
2026-03-02 15:46:21 +01:00
Gijs van Dam
50b34e96ff multi: add sphinx router without replay protection
Initialize a sphinx router without persistent replay protection logging
for onion message processing. Onion messages don't require replay
protection since they don't involve payment routing.
2026-03-02 15:46:21 +01:00
Elle Mouton
1d4c6aadb4
graph/db: thread context through FetchChannelEdgesByOutpoint 2026-02-25 16:11:29 +02:00
bitromortac
0f1472536c
peer+htlcswitch: inject notification endpoint 2026-02-20 10:43:52 +01:00
Yong
e53c4b1de5
Merge pull request #10465 from ziggie1984/bugfix/fix-peer-disconnect-log
peer: fix log output when not applicable
2026-02-11 11:41:20 +08:00
Dario Anongba Varela
6e56c9a538
peer: fix MarkCoopBroadcasted to correctly use local parameter 2026-02-02 12:16:29 +01:00
Olaoluwa Osuntokun
6553b61aa4 peer: send out a notification after the 1st conf, then wait for the rest
We wnt to add better handling, but not break any UIs or wallets. So
we'll continue to send out a notification after a single confirmation,
then send another after things are fully confirmed.
2026-01-15 16:22:26 -08:00
Olaoluwa Osuntokun
c0f48d23b4 multi: add new ChannelCloseConfs param, thread thru as needed
In this commit, we add a new param that'll allow us to scale up the
number of confirmations before we act on a new close. We'll use this
later to improve the current on chain handling logic.
2026-01-15 16:22:26 -08:00
Olaoluwa Osuntokun
931b54e848 peer+rpcserver: use new conf scaling for notifications 2026-01-15 16:22:26 -08:00
András Bánki-Horváth
72ab4d234a
Merge pull request #10289 from GeorgeTsagk/move-aux-closer
Aux Closer: Move coop-close aux finalization to chain watcher
2026-01-13 15:31:34 +01:00
elnosh
34329c684e multi: rename experimental endorsement signal to accountable
Renames the endorsement signal to accountable to
match the latest proposal https://github.com/lightning/blips/pull/67
2026-01-06 09:12:31 -05:00
ziggie
4d1faabd08
peer: fix log output when not applicable
Before we would always log that the peer was not ready starting
up although it was not the case. We now make sure we still log
this case but only when applicable.
2025-12-25 11:35:19 +01:00
George Tsagkarelis
e12008517e
lnwallet+peer: extract close types to separate pkg
The aux close types will soon be used by a different package that would
otherwise cause an import cycle if used directly from
lnwallet/chancloser. We now create a new sub-package lnwallet/types that
will be improrted from all users of these types.
2025-12-04 12:16:54 +01:00
Nishant Bansal
e4c4d946fd
multi: add new config option upfront-shutdown-address
Introduced a new config value `upfront-shutdown-address`
in the `lnd.conf` file. This ensures that channel close
funds are transferred to the specified shutdown address.
The value applies to both the funder and the fundee but
can be overridden by the value specified during
`openchannel` or by the `channel acceptor`.

NOTE: If this field is set when opening a channel with a
peer that does not advertise support for upfront shutdown
feature, the channel open will fail.

Signed-off-by: Nishant Bansal <nishant.bansal.282003@gmail.com>
2025-11-13 19:32:51 +05:30
Gijs van Dam
07dc74e198
multi: endpoints for onion messages
This commit creates the necessary endpoints for onion messages.
Specifically, it adds the following:

- `SendOnionMessage` endpoint to send onion messages.
- `SubscribeOnionMessages` endpoint to subscribe to incoming onion
  messages.

It uses the `msgmux` package to handle the onion messages.
2025-11-12 22:54:04 +08:00
Elle Mouton
b8abe130a5
multi: rename lnwire.NodeAnnouncement
In preparation for adding a NodeAnnouncement2 struct along with a
NodeAnnouncement interface, this commit renames the existing
NodeAnnouncment struct to NodeAnnouncement1.
2025-10-01 13:13:32 +02:00