In this commit, we add validation for channel updates and node
announcements to ensure that we reject gossip messages with zero
timestamps at the discovery layer.
From BOLT 7:
"MUST set timestamp to greater than 0, AND to greater than any
previously-sent channel_update for this short_channel_id."
This validation is performed in the gossip handlers (handleNodeAnnouncement
and handleChanUpdate) rather than at the wire protocol level. This approach
ensures we can still decode messages from disk or embedded in onion errors
while rejecting invalid gossip from peers.
Remote peers sending zero-timestamp gossip will have their ban score
incremented.
In the previous iteration with endorsement
signaling, the recommendation was for the sender to
set it to 1 and that could have had privacy concerns
when first deploying given that the default was to
downgrade the signal to 0. In the latest proposal
the recommended default for both sending and
forwarding nodes is to set `accountable` to 0.
As a result, the dates have been removed given
that there are no privacy risks associated
with relaying the signal with zero values.
This commit fixes a critical race condition in MarkChanFullyClosed and
pruneLinkNode where link nodes could be incorrectly deleted despite
having pending or open channels.
The race occurred because the check for open channels and the link node
deletion happened in separate database transactions:
Thread A: TX1 checks open channels → [] (empty)
Thread A: TX1 commits
Thread B: Opens new channel with same peer
Thread A: TX2 deletes link node (using stale data)
Result: Link node deleted despite pending channel existing
This creates a TOCTOU (time-of-check to time-of-use) vulnerability where
database state changes between reading the channel count and deleting
the node.
Fix for MarkChanFullyClosed:
- Move link node deletion into the same transaction as the channel
closing check, making the check-and-delete operation atomic
Fix for pruneLinkNode:
- Add double-check within the write transaction to verify no channels
were opened since the caller's initial check
- Maintains performance by keeping early return for common case
- Prevents deletion if channels exist at delete time
This ensures the invariant: "link node exists iff channels exist"
is never violated, preventing database corruption and potential
connection issues.
For a Result[T], FlatMap should apply f when the result is Ok, and
propagate the error unchanged when it's Err. The original code returns r
on Ok and tries to use r.left when Err, which is wrong. This commit
fixes that.
Secondly, the group of FlatMap/AndThen and OrElse functions and methods
are now properly tested with new unit tests.
fixes#10401
This commit improves TLV decoding safety and consistency across multiple
packages by enforcing fixed-length requirements and adding unit tests to
prevent malformed TLV records from being accepted.
Changes include:
- lnwire:
* Enforce 8-byte length in Fee TLV decoder.
* Enforce PubNonceSize in Musig2Nonce TLV decoder.
* Enforce 8-byte length in ShortChannelID TLV decoder.
* Added roundtrip and invalid length tests for Fee, Musig2Nonce,
and ShortChannelID records.
- routing/route:
* Enforce Vertex TLV length (33 bytes).
* Added encode/decode and invalid length tests for Vertex.
- tlv:
* Enforce correct length in DBytes33 decoder (33 bytes).
* Added tests ensuring all fixed-size primitive decoders reject
incorrect TLV lengths.
By strictly validating TLV lengths, we prevent malformed or corrupted
TLV records from being silently accepted, improving protocol safety.
In this commit we updated the IsPublicV1Node query to use UNION
instead of OR, since sqlite struggles to efficiently use
multiple indexes in a single query involving OR conditions across
different columns.
We use UNION ALL since the query doesn't care about duplicates.
Fix bug where setSelfNode compared only the seconds component of
timestamps instead of the full timestamp. This caused the node to
attempt persisting an older timestamp than what existed in the
database during restart, resulting in "sql: no rows in result set"
errors.
CI started panicking in TestRbfChannelFlushingTransitions/early_offer /
TestRbfCloseClosingNegotiationRemote (see GitHub Actions run
https://github.com/lightningnetwork/lnd/actions/runs/19155841408/job/54756127218?pr=10352)
because the cached remote offer could fire before the test harness
registered its mock CloseSigner expectations. When that happened,
the mock complained that CreateCloseProposal was unexpected:
panic:
assert: mock: I don't know what to return because the method call was unexpected.
Fix this by having assertSingleRemoteRbfIteration take a sendEvent callback
that receives the context and initial offer, so tests can install expectations
first and then fire the event via SendEvent (or the early-offer test's custom
flush sender).
Reproduction (on master)
------------------------
1. Modify lnwallet/chancloser/rbf_coop_test.go
Add time.Sleep(10 * time.Millisecond) before the first call of
closeHarness.assertSingleRemoteRbfIteration (in function
TestRbfChannelFlushingTransitions).
2. go test ./lnwallet/chancloser -run TestRbfChannelFlushingTransitions/early_offer
3. The panic reproduces immediately.
-Due to a newer version we need to use add_labels instead of just
labels
-The backport PR will now also copy the milestones in case the
milstones were set
This commit adds detailed documentation for the automated backport
workflow and updates the contribution guidelines to reference it.
New documentation (docs/backport-workflow.md):
- Complete overview of the automated backport process
- Step-by-step usage instructions with examples
- Detailed explanation of workflow triggers and label format
- Technical details about workflow implementation
- Conflict resolution procedures and best practices
- Multiple backport scenarios and examples
- Comprehensive troubleshooting guide
Updated contribution guidelines (docs/code_contribution_guidelines.md):
- Replaced detailed backport instructions with brief overview
- Added reference to the new detailed documentation
- Keeps contribution guidelines focused and concise
The detailed documentation provides:
- How to use backport labels correctly
- What happens when labels are added before/after merge
- How the workflow validates branches and handles errors
- Step-by-step conflict resolution instructions
- Solutions for common problems and edge cases
- Examples of valid vs invalid label formats
This documentation ensures contributors and maintainers have clear
guidance on using the automated backport workflow effectively.
This commit introduces an automated GitHub Actions workflow to streamline
the backporting process for merged PRs from master to release branches.
Key features:
- Triggers on merged PRs with labels matching 'backport-v*' pattern
(e.g., backport-v0.20.x-branch)
- Validates that target branches exist before attempting backport
- Creates separate backport PRs for each target branch
- Automatically adds 'no-changelog' label to backport PRs
- Handles merge conflicts by creating draft PRs with conflict markers
- Supports multiple simultaneous backports via multiple labels
Workflow steps:
1. Checkout repository with full git history
2. Validate all target branches exist in the remote repository
3. For each valid backport label:
- Create a new branch (backport-<pr-num>-to-<target-branch>)
- Cherry-pick commits from the master PR
- Create a new PR targeting the release branch
- Link back to the original PR
4. If conflicts occur, create a draft PR for manual resolution
Label format:
- Valid: backport-v0.20.x-branch, backport-v0.19.x-branch
- Invalid: backport candidate, backport-candidate, backport-needed
This automation reduces manual work and ensures consistency in the
backporting process while maintaining full visibility and control
for maintainers.
When creating a missing edge, we need to populate the funding script too
so that the graph builder can update its ChainView appropriately. We use
the MakeFundingScript helper from the funding package which ensures that
we are using the same logic for creating a funding script as is used for
any of the channels that we own.
Fix a race condition where forwarding through a public zero-conf channel
could fail with UnknownNextPeer when using the confirmed SCID. The issue
occurred because ReportShortChanID (which updates the switch's baseIndex
to handle the confirmed SCID) was called AFTER addToGraph (which announces
the confirmed SCID to the network).
With slow backends like postgres, addToGraph takes significant time,
creating a window where other nodes learn about the confirmed SCID from
gossip and attempt to route through it, but the receiving node's switch
hasn't been updated yet to handle forwards using the confirmed SCID.
The fix reorders operations to call ReportShortChanID before addToGraph,
ensuring the switch is ready to handle the confirmed SCID before it's
announced to the network. Forwards using either the alias or confirmed
SCID will work since getLinkByMapping uses baseIndex to map both to the
same link in forwardingIndex.
Fixes flaky test: zero_conf-channel_policy_update_public_zero_conf