Every captured commit receives deterministic security triage and a separate communication-quality score. Security candidates and broader second-pass signals receive full-patch Ollama analysis.
Message quality measures whether a commit identifies its scope, purpose, rationale, testing, and supporting references. It does not change the security-severity score.
This change stops LND from opening new Lightning channels using the old 'legacy' commitment format. The legacy format makes it harder to recover funds if something goes wrong, because the money owed to you is tied to a secret key that chan…
Prevents opening new channels with the legacy commitment type, whose tweaked to_remote output complicates data-loss recoveryCloses a negotiation path where an empty channel_type TLV bypassed feature checks and forced a legacy channelAdds explicit RPC and wire rejection with a dedicated error code
This update fixes a bug in how LND handles old-style punishment transactions when a channel partner tries to cheat. Previously, tiny (dust) HTLCs were left as blank placeholder entries in the punishment data, which could cause the node to …
nil-pointer dereference risk in breach retribution pathlegacy revocation log handling inconsistency with modern formatdefensive hardening added for trimmed/dust HTLCs
This change tightens how LND reads Lightning invoices (BOLT 11). Previously, if an invoice contained more than one payment hash field, LND would silently keep the first one and ignore the rest. Now it rejects the invoice entirely. The rele…
Behavior change from 'use first duplicate field' to 'reject duplicate fields'New error type ErrDuplicatePaymentHash returned on duplicate payment hash fieldsMalformed/unsupported-length duplicate payment hash now treated as duplicate and rejected
This change tightens how LND reads Lightning invoices. Previously, if an invoice contained more than one payment hash field, LND would silently keep the first valid one and ignore the rest. Now it rejects such invoices outright. This preve…
Behavioral change from silent first-field acceptance to explicit rejection of duplicate payment hash fieldsPrevents invoice parsing from depending on field ordering when multiple payment hashes are presentCovers malformed first field + valid second field, closing a potential bypass
This change fixes how LND answers network 'ping' messages from other Lightning nodes. Previously, LND would sometimes silently ignore valid pings to limit reply traffic, which violates the Lightning protocol (BOLT 1) and could make a peer …
Protocol compliance fix: BOLT 1 requires a Pong for every valid PingRemoved silent suppression of Pong replies that could cause peer timeout/misbehaviorUnified rate limiting now accounts for amplification bandwidth, not just ping count
This commit updates LND's integration tests to stop using an obsolete 'legacy' channel type and adds a test confirming that LND now rejects that legacy type. The commit message says the real fix is elsewhere in the same pull request: previ…
Deprecated commitment format (legacy) is now rejected by RPCTest added to verify legacy channel type is refusedUnused legacy node configuration flag removed
This change fixes a bug in LND's handling of AMP (Atomic Multi-Path) payments. Previously, if one partial payment set failed to reconstruct its secret preimage, the entire invoice was canceled, including unrelated partial payment sets from…
Denial-of-service against reusable static AMP invoices by causing unrelated accepted HTLC sets to be canceledIncorrect invoice state transition from set-local failure to global invoice cancellationAvailability impact on multi-payer AMP invoice scenarios
This commit updates the Go programming language version used to build LND from 1.25.13/1.26.6 to 1.26.8 (language version) and 1.27.1 (toolchain). It also regenerates many automatically generated RPC files and adjusts linter settings. The …
Go version bump may indirectly address unlisted runtime/standard-library security fixesLarge regenerated protobuf files suggest dependency/toolchain refresh rather than logic changeLinter configuration expanded to suppress new checks, consistent with a toolchain upgrade
This commit is a routine build-maintenance change. It updates the project's Go linter configuration to a newer version and fixes the resulting style warnings: a WebSocket header spelling and several documentation comments that didn't start…
This commit is a housekeeping change that regenerates automatically generated RPC binding files using a newer Go compiler version. The only differences are in code comments (doc-comment formatting), not in actual program logic or API behav…
This commit is a routine build-and-test maintenance patch to make the LND codebase compile and pass tests with the upcoming Go 1.27 release. It upgrades the project's custom linter tool, refreshes many indirect tool dependencies, adjusts o…
No production code changesNo vulnerability fix or CVE reference presentDependency-only refresh in tools module (development tooling)
This commit is a routine maintenance update that changes which version of the Go programming language is used to build the lnd project. It does not change any application logic, fix a bug, or address a security vulnerability. The project i…
This commit fixes a bug in LND's invoice handling for reusable AMP (Atomic Multi-Path) invoices. Previously, if one payment set failed to reconstruct its preimage, the entire invoice was canceled, even though other valid payment sets on th…
Denial-of-service-like availability impact: a malicious or buggy payer could cause a whole reusable AMP invoice to be canceled, blocking legitimate concurrent payersSet-local failure incorrectly escalated to invoice-global cancellationRegression test added for concurrent-set survival after reconstruction failure
This commit adds the missing cryptographic signature layer for BOLT 12 offers in LND. It builds a Merkle tree from the message fields, signs that tree with a BIP-340 Schnorr signature, and now rejects invoice requests and invoices whose si…
Adds BIP-340 Schnorr signature verification for BOLT 12 invoice_request and invoice messagesMerkle tree commits to canonical re-encoded TLV records; non-minimal feature/amount encodings now rejected to preserve byte-exactnessSignature TLV type 240 and reserved range 240-1000 excluded from the signed Merkle root
This commit adds a new optional wallet feature to LND that lets users lock UTXOs until the transaction spending them reaches a chosen confirmation depth, instead of relying on a wall-clock timeout. It is a feature addition with safety chec…
New RPC fields release_after_spend_confs and confirmed_spend_height added to LeaseOutput and FundPsbtCustom lock ID enforced as required for confirmation-controlled FundPsbt leases; all-zero and reserved internal IDs rejectedCapability check (ResolveOutputLeaser) prevents silent downgrade to time-only lease when confirmation control is requested
This commit is a large internal code reorganization for the LND Lightning node. It moves channel state storage logic from the `channeldb` package into a new `chanstate` package, while keeping the same database keys, serialization formats, …
Large refactoring with no change to cryptographic or consensus-critical logicCodec serialization moved verbatim to new package; wire format unchangedDatabase bucket keys and TLV record types preserved
This change fixes a data-handling bug in LND's channel-opening code. When opening a Lightning channel using an optional 'auxiliary funding controller' (used for custom channel types such as Taproot Assets), the funding manager was previous…
Data-correctness fix in funding handshake: negotiated channel configs now exposed to auxiliary funding controllerAuxiliary funding controller receives incomplete channel state before patch, complete state after patchNo explicit security claim, CVE, or advisory referenced in commit or supplied materials
This change makes LND's BOLT 12 invoice and invoice-request reader actually check that the digital signature is valid, instead of only checking that a signature field exists. Before, an attacker could craft or modify a BOLT 12 message and …
Missing cryptographic verification on parsed BOLT 12 messages replaced with BIP-340 Schnorr signature verificationReader accepted any 64-byte placeholder signature before the patchNew negative tests verify rejection of wrong-signer and tampered Merkle-root fields
This commit only adds a new integration test for an existing feature called confirmation-controlled leases. It does not change production code, so it cannot introduce a security vulnerability or fix one directly. The test exercises how lon…
This update fixes two security-related bugs in LND's REST WebSocket proxy. First, a specially crafted WebSocket protocol header could crash the proxy (a panic). Second, the proxy previously had no limit on how large an incoming WebSocket m…
panic fix in request header parsingunbounded incoming WebSocket message read now cappedincorrect header value assignment possible before fix
lnwallet/rpcwallet: accept zero-value WitnessUtxo entries in remote-sign prep
Before forwarding a SignOutputRaw request to the remote signer instance, remoteSign rebuilds a PSBT from the unsigned transaction and annotates every input with a WitnessUtxo (so the downstream walletkit.SignPsbt call accepts it — taproot sighash computation requires the prev output of every input, not just the one being signed).
For non-signed inputs the prep stage first asks the watch-only wallet about the outpoint via FetchOutpointInfo, then — when the wallet does not own or track the outpoint — falls back to the sign descriptor's PrevOutputFetcher. The fallback previously required `utxo.Value != 0`, which silently dropped legitimate zero-value entries on the floor and left the corresponding PSBT input bare.
The walletkit.SignPsbt entry point on the remote signer then rejected the PSBT with "input (index=N) doesn't specify any UTXO info" because input N had neither a WitnessUtxo nor a NonWitnessUtxo annotation.
BIP-322 (signing virtual transactions for message attestation) is the canonical hitter: its to_spend output is mandated by the BIP to be exactly value=0 with the message commitment as pk_script, and that output is referenced as input 0 of every BIP-322 to_sign transaction. Any caller that drives a BIP-322 sign through a remote-signer LND deployment was failing for this reason.
The validation we actually want is that the fetched prev output is representable as a usable WitnessUtxo: non-nil and with a non-empty pk_script. Drop the Value check; the zero-value case is well-formed and the resulting PSBT input will serialize cleanly. The fetched-but- empty-pk_script case continues to be rejected (a WitnessUtxo with empty PkScript is malformed at PSBT serialization), and the warning log when no fallback resolves the outpoint is preserved verbatim.
Lift the WitnessUtxo-population loop out of remoteSign into a package-level helper so the resolution policy is unit-testable without spinning up a real wallet + remote signer pair. The helper takes a fetchOutpointInfoFn callback that mirrors lnwallet.WalletController.FetchOutpointInfo. No behavior change for the wallet-owns-it path or the no-fallback path.
85/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode
Why it was queued
signing boundaryupdate trustsigning or wallet path
AI analysis · Low 43/100
This commit fixes a bug in LND's remote-signing setup where zero-value previous transaction outputs were wrongly ignored when preparing a PSBT for a remote signer. The most concrete affected use case is BIP-322 message attestation, where the protocol requires a zero-value output. Because of the bug, the remote signer would refuse to sign, causing a functional failure rather than a loss of funds. The patch removes the mistaken zero-value check and adds a unit-testable helper function.
Security candidatelnwallet/rpcwallet: add unit tests for WitnessUtxo population helperby Olaoluwa Osuntokun · 6bb2c6f5 · May 19, 2026 · 1 fileMessage 83 · StrongInformational 15Details
Commit message · Olaoluwa Osuntokun
lnwallet/rpcwallet: add unit tests for WitnessUtxo population helper
Cover the four resolution branches plus the BIP-322 regression case:
- wallet-owns-it: FetchOutpointInfo returns a Utxo, helper writes the matching WitnessUtxo into the PSBT input. - external-fallback: wallet returns ErrNotMine, helper writes the WitnessUtxo from the sign descriptor's PrevOutputFetcher. - zero-value-fallback: same as above with the fetched entry's Value set to zero. This is the BIP-322 to_spend shape (input 0 of every BIP-322 to_sign references a virtual prev whose Value is mandated to be zero); the helper must populate the WitnessUtxo rather than silently skip it. - no-fallback: wallet returns ErrNotMine and no PrevOutputFetcher is provided; the helper leaves the input bare and the warning log fires (asserted only by absence of a populated WitnessUtxo). - empty-pk_script-fallback: the fetcher returns a non-nil entry with an empty PkScript; the helper rejects it as unusable (the PSBT WitnessUtxo serializer requires a non-empty script) and leaves the input bare.
The signed input (signDesc.InputIndex) is intentionally left untouched by the helper — that input is the one the caller's main path will populate later — and the tests cross-check that invariant on the wallet-owns-it case.
83/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
signing boundarysigning or wallet path
AI analysis · Informational 15/100
This commit only adds new unit tests for an existing helper function in LND's RPC wallet code. It does not change any production logic, so it cannot introduce a security vulnerability or directly fix one in the code being committed. The tests document and verify how the helper populates PSBT witness UTXO data, including a regression test for BIP-322 zero-value outputs.
lnrpc: remove deprecated Send* RPC server implementations
Remove handler implementations and macaroon permission entries for the now-deleted lnrpc RPCs: SendPayment, SendPaymentSync, SendToRoute, and SendToRouteSync.
Also remove the dead payment infrastructure that was exclusively used by these handlers: paymentStream, rpcPaymentRequest, rpcPaymentIntent, extractPaymentIntent, dispatchPaymentIntent, sendPayment, and sendPaymentSync.
73/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context
Why it was queued
credential or privilege state
AI analysis · Informational 15/100
This commit removes old, unused code paths for sending Lightning payments through the main RPC server. These RPCs (SendPayment, SendPaymentSync, SendToRoute, SendToRouteSync) were already deleted from the service definition in a prior change, so this patch only cleans up the leftover server-side implementations and their macaroon permissions. There is no security vulnerability here; it is routine code deletion of dead functionality.
Remove the SendPayment, SendToRoute, and TrackPayment shim methods from router_server_deprecated.go that delegated to their V2 counterparts. Remove their macaroon permission entries from router_server.go and the now-unused legacyTrackPaymentServer wrapper.
73/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context
Why it was queued
credential or privilege state
AI analysis · Low 37/100
This commit removes three old, deprecated payment RPC methods (SendPayment, SendToRoute, and TrackPayment) from the LND Lightning node's router service. These methods were already replaced by newer V2 versions and were only thin wrappers around them. Removing them reduces the amount of code that could contain bugs and removes their access permissions, but it may also break older client software that still calls these methods.
Security candidatedocs: add v0.21.0 RC testing guides for humans and agentsby saubyk · 2d0187c6 · May 13, 2026 · 12 filesMessage 95 · StrongInformational 15Details
Commit message · saubyk
docs: add v0.21.0 RC testing guides for humans and agents
Adds docs/testing-guides/v0.21.0/ with a per-feature guide for the v0.21.0-beta.rc1 release. Each guide follows a fixed template (prereqs, setup, scenarios with concrete pass/fail signals, failure investigation) so RC testers and automated agents can work through them predictably.
This is a first draft intended for community review on the PR. payment-sql-migration.md carries a TBD callout for the SkipNativeSQLMigration rescue-path behavior, pending developer confirmation.
95/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Uses a recognizable type or scope✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
update trustdocumentation-only discount
AI analysis · Informational 15/100
This commit only adds documentation: a set of release-candidate testing guides for the upcoming LND v0.21.0 release. No source code, configuration defaults, RPC behavior, or database logic is changed. The new Markdown files describe how testers (human or automated) can verify new features such as taproot channels, onion-message rate limiting, and a payment-store migration. Because nothing executable is modified, the commit itself does not introduce a security vulnerability or fix one.
lntest: pin pre-v30 mempool policy defaults in itest bitcoind
Bitcoind v30 lowered the default minrelaytxfee and incrementalrelayfee from 1000 sat/kvB (1 sat/vB) to 100 sat/kvB. The itest suite was written against the old defaults and the lower values cascade into:
- integer sat/vByte assertions losing precision below 1 sat/vB, and - RBF bump thresholds that alter sweeper/bumpfee replacement timing.
Pin the old defaults in the itest bitcoind backend so the existing tests keep passing without per-test adaptation. Running against the new defaults is still worth doing, but that is a separate exercise that should not be bundled with the v30 version bump.
83/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
access control
AI analysis · Informational 14/100
This change only adjusts the settings used for internal testing of LND with a Bitcoin Core backend. It pins older Bitcoin Core fee defaults so that existing automated tests continue to produce the same results after Bitcoin Core v30 changed its defaults. It does not change production LND code, user-facing behavior, or network consensus rules, and it does not fix a security vulnerability.
The existing per-peer actor mailbox (capacity 50, RED from depth 40) only bounds in-flight queue depth. It does not cap throughput: a peer that drains its actor quickly can saturate our Sphinx unwrap CPU, replay-DB writes, and outbound forwarding bandwidth without ever tripping RED. At spec-max onion message sizes (~32 KiB per sphinx packet) a single well-behaved-draining peer is enough to push multiple Mbps of unpaid forwarded traffic through us, and aggregate fan-in from many peers multiplies that into tens of Mbps — an amount of bandwidth that is very much out of proportion for a side channel on a payment routing node.
This commit adds the building blocks for two token-bucket limiters that will be wired into the onion message ingress path in a follow-up commit: a process-wide global limiter and a per-peer registry. Both drop (rather than wait) on over-limit so that a hostile peer cannot grow our goroutine or memory footprint simply by sustaining above-threshold traffic. The per-peer registry keys buckets on the peer's compressed pubkey, creates them lazily, and retains them for the lifetime of the process so a peer cannot reset its burst by cycling the connection; cardinality is bounded by the live channel-peer count (the ingress call site gates on having a channel before allocating per-peer state), so no time-based GC is needed.
A minimal RateLimiter interface is introduced so that callers and tests can substitute noop or alternate implementations without reaching into x/time/rate directly, and a small countingLimiter wrapper keeps an atomic drop counter plus a one-shot first-drop flag for observability. A rate of zero (or a non-positive burst) yields a noop limiter, providing a clean "disabled" mode without branching at the call site.
On top of those, a single IngressLimiter interface composes the per-peer and global buckets behind one surface so that callers — notably the peer readHandler — only thread one object through Config and call one method per incoming onion message. Drop reasons are surfaced as sentinel errors (ErrPeerRateLimit, ErrGlobalRateLimit) wrapped in fn.Result[fn.Unit] so callers match on them with errors.Is rather than comparing free-form strings. The stock implementation encodes the load-bearing ordering — per-peer first, then global — inside AllowN so that a hostile peer whose own bucket is already empty cannot burn global tokens on every rejected attempt and starve legitimate peers.
Default constants targeting roughly ~5 Mbps worst-case ingress at spec-max message sizes are added alongside the existing mailbox defaults.
95/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode✓ Mentions testing or verification
Why it was queued
secure hardware boundary
AI analysis · Moderate 61/100
This commit adds new rate-limiting building blocks for LND's onion message handling. It does not yet wire them into live message processing, so by itself it cannot stop an attack. The code is clearly preparing to fix a denial-of-service risk: a single peer (or many peers together) could currently flood a routing node with large, unpaid onion messages and consume CPU, database writes, and outbound bandwidth. The new primitives cap per-peer and total incoming onion-message bytes using token buckets, and they drop excess traffic immediately rather than queuing it.
Security candidatepeer: gate onion message ingress on having an open channelby Olaoluwa Osuntokun · c0827e8e · Apr 15, 2026 · 9 filesMessage 95 · StrongHigh 76Details
Commit message · Olaoluwa Osuntokun
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.
95/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode✓ Mentions testing or verification
This change closes a denial-of-service weakness in LND's onion-message forwarding. Previously, an attacker could create unlimited free peer identities and burn through the global byte-budget reserved for onion messages, starving real peers. The patch now requires a peer to share at least one funded, fully open Lightning channel before any onion message is accepted, so each attacker identity must lock up real bitcoin. It also adds a fast O(1) atomic counter so this check does not slow down every incoming message.
lnwallet: regenerate taproot test vectors with secret nonces
Regenerate the test vectors JSON to include local_sec_nonce and remote_sec_nonce fields alongside the existing public nonces. The local nonce fields now correctly correspond to local's verification nonce for their own commitment transaction, matching the commitment tx stored in the test vector.
83/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
defensive validationfuzzing or regression evidencenonce handlingsigning or wallet path
AI analysis · Informational 12/100
This commit only updates test data in a JSON file used for automated tests of Lightning's new taproot channel features. It adds secret nonce values and corrects public nonce values so the test vectors match the expected commitment transactions. There is no change to production wallet or network code, so it does not introduce a security vulnerability or fix one in running software.
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.
88/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification✓ Names security-relevant behavior explicitly
Why it was queued
entropy or randomnesssigning boundaryfuzzing or regression evidencesigning or wallet path
AI analysis · Informational 19/100
This change adds a hidden switch that lets developers plug in a custom random source when creating MuSig2 signing nonces, mainly so tests can produce exactly the same signatures every time. In normal operation the switch is left empty, so the code still uses the operating system's cryptographic random generator. The patch itself is a test-infrastructure feature, not a fix for an active security bug, but any future misuse of the switch could weaken signature security.
lnwallet: add taproot channel test vector generator
In this commit, we add a test vector generator and verifier for taproot channel constructions. All vectors are derived deterministically from a single 32-byte seed using SHA256(seed || label) for key derivation, ensuring any implementation can reproduce them independently.
The generator covers two areas:
Script vectors decompose the full tapscript trees for every output type: funding (MuSig2 aggregated key), to_local (delay + revocation leaves), to_remote (1-block CSV leaf), anchors (OP_16 OP_CSV), offered/accepted HTLCs on both local and remote commits, and second-level HTLC transactions. Each entry captures the raw leaf scripts, leaf hashes, tapscript root, internal key, output key, and pkScript.
Transaction vectors produce full serialized commitment transactions and HTLC resolution transactions for three scenarios: a simple commitment with no HTLCs, a commitment with five untrimmed HTLCs, and the same HTLCs at a higher fee rate causing some to be trimmed.
To generate: go test -run TestTaprootVectors ./lnwallet/ -args -generate-taproot-vectors To verify: go test -run TestTaprootVectors ./lnwallet/
83/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
secret or key materialfuzzing or regression evidencesigning or wallet path
AI analysis · Informational 15/100
This commit only adds new test code and a JSON file of expected test outputs for Taproot Lightning channels. It does not change any production logic, network behavior, or wallet handling. There is no security issue in the commit itself.
lnwallet: add 3rd-party signature verification for taproot test vectors
In this commit, we add a `signature_verification` sub-test to the taproot test vector verifier that performs full script execution against both the commitment transaction and all HTLC resolution transactions.
This uses `txscript.NewEngine` to execute the taproot witness programs exactly as a Bitcoin node would, providing an independent check that all signatures in the test vectors are cryptographically valid. For the commitment transaction, we verify its witness against the funding output pkScript. For each HTLC resolution transaction, we verify its witness against the corresponding commitment output it spends.
This catches issues that the structural comparison tests (hex matching) cannot: for instance, a transaction can have the correct structure but carry an invalid signature if the sighash was computed over the wrong prevout or if the wrong key was used for signing. Running the full script engine also validates the control block, the tap leaf hash, and the overall taproot spend path.
88/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification✓ Names security-relevant behavior explicitly
Why it was queued
signing boundarydefensive validationfuzzing or regression evidencesigning or wallet path
AI analysis · Informational 12/100
This commit only adds a new test to the project's test suite. It does not change any production code, user-facing behavior, or network protocol. The new test cryptographically checks that example transactions in the project's test data carry valid Bitcoin signatures. It is a defensive quality improvement, not a fix for a known bug or vulnerability.
lnwallet: regenerate test vectors with BIP-340 HTLC signatures
Regenerate the test vector JSON with HTLC second-level transaction signatures that use BIP-340 standard nonce derivation (zero auxrand) instead of RFC6979. This makes the HTLC signatures reproducible across different Schnorr implementations. The commitment transaction MuSig2 signatures are unchanged.
83/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
signing boundaryfuzzing or regression evidencesigning or wallet path
AI analysis · Informational 15/100
This commit only updates test data in a single JSON file. It changes the example signatures used in automated tests so they are generated with a standard BIP-340 nonce method instead of an older RFC6979 method. No production wallet code, protocol logic, or user-facing behavior is changed. There is no security vulnerability here.
Security candidatelnwallet: emit actual MuSig2 partial sigs and nonces in test vectorsby Olaoluwa Osuntokun · fa97946f · Apr 13, 2026 · 1 fileMessage 88 · StrongInformational 18Details
Commit message · Olaoluwa Osuntokun
lnwallet: emit actual MuSig2 partial sigs and nonces in test vectors
In this commit, we fix the taproot test vector generator to capture and emit the real MuSig2 partial signatures and public nonces rather than the dummy `CommitSig` value which is zeroed out for taproot channels.
Previously, the generator was reading from `CommitSig.ToSignatureBytes()` which yielded a minimal DER encoding of `(0, 0)` (the 8-byte string `3006020100020100`). For taproot channels the actual signature lives in the `PartialSig` field of the `CommitSigs` struct, which carries both the 32-byte partial sig scalar and the 66-byte compressed public nonce needed by the verifier to reconstruct the combined signature.
We now unwrap the `PartialSig` from both the local and remote commitment signatures, extract the nonce and sig bytes, and include `local_nonce` and `remote_nonce` fields alongside `remote_partial_sig` in the emitted JSON. This gives other implementations (eclair, CLN, etc.) all the material they need to independently verify commitment transaction signatures using their own MuSig2 libraries.
88/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification✓ Names security-relevant behavior explicitly
Why it was queued
signing boundaryfuzzing or regression evidencesigning or wallet path
AI analysis · Informational 18/100
This commit fixes a test-data generator used by the LND Lightning node. Previously, the generator wrote a placeholder zero-value signature into its JSON test vectors for Taproot channels. Now it writes the real partial signature and public nonce values that other Lightning implementations need to independently check those test vectors. This is a test tooling fix, not a live security bug in production code.
Regenerate `test_vectors_taproot.json` to reflect the corrected test vector generator. Changes include actual 32-byte MuSig2 partial signatures (replacing the dummy 8-byte DER stubs), 66-byte public nonces for both local and remote parties, corrected HTLC sig-to- transaction mapping sorted by BIP 69 output index, proper HTLC-success witness layout with preimage in the correct witness slot, and the updated trimming test case which now trims 3 of the 5 test HTLCs below the 2500 sat dust limit (down from 5 HTLC outputs to 2).
78/100 · AdequateMessage clarity
✓ Descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
signing boundaryfuzzing or regression evidencesigning or wallet path
AI analysis · Informational 18/100
This commit only updates a single JSON file of test vectors for taproot Lightning channels. It replaces placeholder signature data with realistic values, fixes the order of test transactions, and adjusts a dust-limit test case. There is no change to production wallet or node code, so it does not directly affect live funds or network security. It is essentially a correction to the project's test fixtures.
Security candidatelnwallet: add secret nonce stashing to MusigSession for test vectorsby Olaoluwa Osuntokun · 2148445c · Apr 13, 2026 · 1 fileMessage 83 · StrongInformational 18Details
Commit message · Olaoluwa Osuntokun
lnwallet: add secret nonce stashing to MusigSession for test vectors
In this commit, we add the ability for MusigSession to capture and expose the raw 97-byte MuSig2 secret nonce generated during JIT signing nonce creation. This is gated behind the customNonceRand option, so it only activates in test vector generation mode.
The stashed secret nonce is consumed on read (cleared after access) to prevent accidental nonce reuse. This enables interop test vectors to include the raw secret nonces, allowing other implementations to replay the MuSig2 signing process without needing to match the exact nonce derivation algorithm used by btcd's musig2 library.
83/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
signing boundaryfuzzing or regression evidencenonce handlingsigning or wallet path
AI analysis · Informational 18/100
This commit adds a test-only feature that lets LND temporarily save the secret random number (nonce) used in MuSig2 signing when running in a special test-vector generation mode. The saved value is automatically erased after one read to prevent accidental reuse. There is no indication this feature is active in normal production code, and the change is explicitly described as being for interoperability test vectors only.
Security candidatelnwallet: add MuSig2 secret nonces and partial sig replay to test vectorsby Olaoluwa Osuntokun · 4c225ddf · Apr 13, 2026 · 1 fileMessage 88 · StrongInformational 12Details
Commit message · Olaoluwa Osuntokun
lnwallet: add MuSig2 secret nonces and partial sig replay to test vectors
In this commit, we extend the taproot test vector generator and verifier to include MuSig2 secret nonces and a full partial signature replay test.
For the generator, we now capture the correct nonces for each commitment transaction: local's verification nonce (from LocalSession) for local's own commitment, and remote's JIT signing nonce (from RemoteSession) for the same commitment. Previously, the local nonce was incorrectly captured from the RemoteSession, which corresponds to a different commitment transaction.
The new musig2_partial_sig_replay test sub-suite verifies three properties for each test case:
1. The remote partial sig can be independently reproduced from the secret nonce and private key using musig2.Sign().
2. The local partial sig can be independently produced and verified using the local secret nonce.
3. Both partial sigs combine (via the Session API) into the exact Schnorr signature present in the commitment transaction witness.
This enables interop implementations to validate their MuSig2 signing logic against the test vectors without needing to match nonce derivation algorithms across different secp256k1 libraries.
88/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification✓ Names security-relevant behavior explicitly
Why it was queued
secret or key materialsigning boundarydefensive validationfuzzing or regression evidencenonce handlingsigning or wallet path
AI analysis · Informational 12/100
This commit only changes test code. It improves the test-vector generator for taproot channels so it records the correct MuSig2 secret nonces, and adds a new test that replays the signing process to confirm the produced signatures match the ones stored in the test vectors. There is no change to production wallet or signing logic, so it does not introduce or fix a live security vulnerability.
Security candidatelnwallet: use BIP-340 nonce derivation for HTLC sigs in test vectorsby Olaoluwa Osuntokun · 63450b85 · Apr 13, 2026 · 1 fileMessage 88 · StrongInformational 19Details
Commit message · Olaoluwa Osuntokun
lnwallet: use BIP-340 nonce derivation for HTLC sigs in test vectors
btcd's schnorr.Sign defaults to RFC6979 nonce derivation, while libsecp256k1 (used by eclair, CLN, etc) uses BIP-340's standard nonce derivation with zero auxrand. Both are deterministic but produce different signatures for the same key and message, causing HTLC signature mismatches in interop test vectors.
This commit introduces a bip340Signer wrapper that overrides SignOutputRaw for taproot script path spends to use schnorr.CustomNonce([32]byte{}) — matching BIP-340 deterministic signing behavior. The wrapper is only used in the test vector generator; production signing paths are unchanged.
Note that MuSig2 commitment signatures were already using BIP-340 nonces internally (via the musig2.Sign path), so only the HTLC second-level transaction signatures were affected.
88/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification✓ Names security-relevant behavior explicitly
Why it was queued
signing boundaryfuzzing or regression evidencesigning or wallet path
AI analysis · Informational 19/100
This commit changes only a test file in LND. It makes the test-vector generator produce HTLC signatures using the same nonce math as other Lightning implementations (Eclair, Core Lightning), so cross-implementation test vectors match. Production signing code is not touched, so real user funds or network behavior are unaffected. There is no security vulnerability being fixed here; it is a test-infrastructure consistency change.
lnwallet: fix HTLC sig-to-transaction mapping in test vector generator
In this commit, we fix two interrelated bugs in the way HTLC signatures are associated with their corresponding second-level transactions in the taproot test vector generator.
The first issue was that HtlcSigs are sorted by BIP 69 output index (matching the commitment transaction's output ordering), but the old code was assigning signatures using the iteration order of incoming HTLCs followed by outgoing HTLCs. This meant timeout transaction signatures were getting paired with success transactions and vice versa whenever the output ordering didn't happen to match the incoming-first iteration order. This is the root cause of the invalid HTLC-timeout signatures that eclair reported when cross-validating.
We now collect all HTLC entries (both incoming and outgoing) into a single slice, sort them by their commitment output index, then zip them against the HtlcSigs array so each signature lines up with the correct second-level transaction.
The second issue was in the HTLC-success preimage extraction path. The old code read the witness script from index [4] (the control block) and used a hardcoded byte offset of 69 to locate the payment hash, then wrote the preimage into index [3] (overwriting the script). The correct taproot witness layout is [remoteSig, localSig, preimage, script, controlBlock], so the script lives at [3] and the preimage slot is [2]. We now use `txscript.ScriptTokenizer` to walk the script opcodes and find OP_HASH160 followed by the 20-byte push data, which is far more robust than relying on fragile byte offsets that break if the script template ever changes.
100/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode✓ Mentions testing or verification✓ Names security-relevant behavior explicitly
Why it was queued
signing boundaryfuzzing or regression evidencesigning or wallet path
AI analysis · Informational 24/100
This commit fixes bugs in a test-data generator used by LND, not in the live Lightning node software itself. The generator produces sample HTLC (multi-hop payment) resolution transactions and signatures that other implementations use to cross-check compatibility. The old code paired signatures with the wrong transactions when HTLC outputs were not ordered 'incoming first,' and it also read preimages from the wrong witness slot using a hardcoded byte offset. These bugs caused invalid test vectors, which another implementation (Eclair) noticed during cross-validation. The fix only changes test code, so it does not create or close a real attack path against running LND nodes.
input: add production taproot HTLC succeed input constructor
This commit adds MakeTaprootHtlcSucceedInputFinal, a new input constructor specifically for creating HTLC success inputs that use production taproot witness types. This function parallels the existing MakeTaprootHtlcSucceedInput but creates inputs with the TaprootHtlcAcceptedRemoteSuccessFinal witness type instead of the staging variant.
The new constructor follows the same pattern and signature as its staging counterpart, ensuring consistency in the input creation API. This allows contract resolvers to create the appropriate input type based on whether they are handling a staging or production taproot channel, ensuring that the correct witness generation logic is applied during transaction creation.
This addition provides the necessary infrastructure for production taproot channels to properly construct inputs for sweeping HTLC outputs on remote commitment transactions with the optimized script structure.
78/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Names security-relevant behavior explicitly
Why it was queued
signing boundary
AI analysis · Informational 17/100
This commit adds a new helper function in LND for creating a specific kind of Bitcoin transaction input used when claiming a Lightning HTLC (a payment locked by a hash/time condition) on a production taproot channel. It mirrors an existing staging-channel helper but uses the final/production witness type. There is no direct evidence in the commit of a security vulnerability; it appears to be infrastructure for an upcoming or recently enabled feature.
actor: add CompleteWith and AwaitFuture generic package-level helpers
In this commit, we add two package-level generic helpers to the actor module as part of the broader chan error -> Future[error] migration in the discovery package.
CompleteWith[T](p Promise[T], val T) is a one-liner convenience wrapper over p.Complete(fn.Ok(val)). It lets callers complete a promise with a plain value without constructing an fn.Result inline, which cuts noise at every completion site.
AwaitFuture[T](ctx, f Future[T]) (T, error) provides the symmetric receive side: it blocks until the future resolves or the context is cancelled and returns the value and any context error unpacked from the fn.Result, matching the (val, err) convention callers expect.
Both functions are deliberately thin (no policy, no timeout, no new state) so they compose freely with higher-level helpers built on top, e.g. discovery.AwaitGossipResult.
go.mod is updated to pin the actor module via a local replace directive so the rest of the lnd module picks up these additions without waiting for a tagged release.
73/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context
Why it was queued
access control
AI analysis · Informational 15/100
This commit adds two small helper functions to an internal actor package and updates the Go module file to use the local copy of that package. There is no security-relevant change; it is purely a code cleanup and convenience addition for future development work.
Security candidategraph/db: add gossip version parameter to FilterKnownChanIDsby Elle Mouton · 12f8e509 · Apr 9, 2026 · 5 filesMessage 78 · AdequateLow 27Details
Commit message · Elle Mouton
graph/db: add gossip version parameter to FilterKnownChanIDs
Add an explicit lnwire.GossipVersion parameter to FilterKnownChanIDs on the Store interface, SQLStore, KVStore, and ChannelGraph. Since FilterKnownChanIDs is always called from a version-scoped ChanSeries context, a single version parameter is cleaner than per-item version reads. A convenience wrapper on VersionedGraph preserves the existing ChanSeries call-site signature by threading c.v automatically.
78/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Names security-relevant behavior explicitly
Why it was queued
signing boundary
AI analysis · Low 27/100
This commit refactors how a Lightning Network routing database method, FilterKnownChanIDs, receives the gossip protocol version. Previously the version was read from each individual channel record; now it is passed explicitly from the version-scoped wrapper. The change also adds a sanity check that zombie channels returned by the database match the expected gossip version, and updates tests to cover both v1 and v2 gossip. It is a defensive correctness improvement rather than a clear-cut fix for an active vulnerability.
Security candidateci: use GITHUB_TOKEN instead of PAT for PR severity workflowby ziggie · 77c566f2 · Apr 8, 2026 · 1 fileMessage 97 · StrongLow 32Details
Commit message · ziggie
ci: use GITHUB_TOKEN instead of PAT for PR severity workflow
The PR severity classifier only needs to run `gh pr view`, `gh pr edit` (labels), and `gh pr comment`. All three operations are fully covered by the built-in GITHUB_TOKEN given the existing permissions block:
The workflow uses `pull_request_target`, which runs in the base repo context, so GITHUB_TOKEN has write access even for fork PRs.
Inspection of the claude-code-action@v1 source confirmed that the only internal call that would require `contents: write` is branch deletion, which is never exercised here because Claude's tools are locked down to `gh pr view/edit/comment` via --allowedTools.
This removes the dependency on the PR_SEVERITY_BOT_TOKEN PAT secret.
97/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Uses a recognizable type or scope✓ Provides detailed explanatory context✓ Explains rationale or failure mode
Why it was queued
credential or privilege statedocumentation-only discount
AI analysis · Low 32/100
This commit changes a GitHub Actions workflow so it uses the automatically provided GITHUB_TOKEN instead of a long-lived personal access token (PAT) named PR_SEVERITY_BOT_TOKEN. The workflow labels and comments on pull requests. The change reduces the risk that a leaked or overly powerful PAT could be misused, and it is a security-hardening improvement rather than a fix for an active vulnerability.
graph/db: convert TestLightningNodeSigVerification to versioned test
Rename to testLightningNodeSigVerification and add it to the versionedTests table so it runs against both v1 and v2. The signing step is version-specific (ECDSA for v1, Schnorr for v2) while the verification path is shared.
83/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
signing boundarydefensive validation
AI analysis · Informational 15/100
This commit only changes a test file. It renames an existing test and makes it run against two versions of the software's gossip protocol, adding Schnorr signature coverage for the newer version. There is no change to production code, so it does not introduce or fix a security vulnerability in the running software.
lnwire: validate MuSig2 nonces in LocalNoncesData decode
Validate each nonce in decodeLocalNoncesData with ValidateMusig2Nonce to reject malformed entries.
65/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides an explanatory body
Why it was queued
defensive validationnonce handling
AI analysis · Moderate 59/100
This change adds a safety check when LND reads stored MuSig2 cryptographic nonces from disk or network data. Previously, a malformed or all-zero nonce could be loaded silently; now the decoder rejects it. MuSig2 nonces are used in multi-signature Bitcoin transactions for the Lightning Network, so bad nonces could in theory lead to failed channel operations or, in worst-case cryptographic scenarios, weaken security guarantees.