Previously we had db and application logic mixed on the db level.
We now move the config option KeepFailedPaymentAttempts to the
ChannelRouter level and move it out of the db level.
We make the TestDeleteNonInFlight and separate all the logic out
for the duplicate payment test case. The deletion of duplicate
payments is now tested in isolation only for the kv backend.
The design of the sql and kv db are a bit different. A harness
interface is introduced which allows us to unit most of the test
and keep the backend specific tests at a minimum.
In commit adds the harness which will be used to run db agnostic
tests against the kv and sql backend. We have adopted all the
unit tests so far so that with this commit all the payment tests
not specifically put into the kv_store_test.go should all pass
for all backends.
We are now not supporting the LegacyPayload for the onion packet
anymore. All payments and their onion payload need to be tlv
encoded. The sql backend assumes tlv so we have to always set the
in memory presentation of a hop where the legacy parameter is still
available but deprecated to false, otherwise the hops will not be
equal and unit tests for the sql backend will fail when switched
on in the next commits.
Since now the sql backend is more strict in using the same
session key we refactor the helper so that we can easily change
the session key for every new attempt.
Now that every method of the interface was implemented we can
remove the embedded reference we put into place for the sql store
implementation so that the interface would succeed. This is now
removed.
We rename this variable to paymentsDetailsData because we will
also need to batch load the core payment and intent data in
future commits and this renaming should make it clear that this
does match payment related data but not the core data which is
in the payment and in the intent table.
We remove the SQLStore from most of the helper functions. This
also makes sure we do not accidentally create a new db tx but use
the provided db SQLQueries parameter.
Previously a one(intent)-to-many(payment) relationship it is now
changed to a one-to-one relationship because a payment request
only can have 1 payment related to it. Looking into the future
with BOLT12 offers, the fetched invoice from the offer could be
stored here as well and the relationship would still hold.
We prepare the code for the sql payment backend. However no
payment db interface method for the sql backend is implemented
yet. This will be done in the following commits. They currently
use the embedded KVStore to satify the build environment.
Add testFetchPendingInvoicesAccepted to explicitly verify the state
filtering behaviour of FetchPendingInvoices across all four contract
states:
ContractOpen (state 0) – must be returned
ContractAccepted (state 3) – must be returned
ContractSettled (state 1) – must be excluded
ContractCanceled (state 2) – must be excluded
This directly exercises the `state IN (0, 3)` predicate introduced in
the FetchPendingInvoices SQL query and addresses the review request for
explicit test coverage of the new targeted query variants. The test
runs against the KV, SQLite, and Postgres backends.
All call sites have been migrated to the targeted replacement queries
in the previous commit. Remove FilterInvoices and its associated
params struct from the SQL source and regenerate.
Replace all four FilterInvoices call sites with the focused queries
introduced in the previous commit:
FetchPendingInvoices → FetchPendingInvoices
InvoicesSettledSince → FilterInvoicesBySettleIndex
InvoicesAddedSince → FilterInvoicesByAddIndex
QueryInvoices → FilterInvoicesForward / FilterInvoicesReverse
The first three are straight 1:1 swaps — the new params structs carry
only the fields that are actually used, and the removed fields (Reverse,
PendingOnly, unused index bounds) were always left at their zero values.
QueryInvoices is restructured more substantially. The forward/reverse
branch now selects between FilterInvoicesForward and
FilterInvoicesReverse, each of which takes a concrete id bound that is
always set:
forward: AddIndexGet = IndexOffset + 1 (≥ 1 when IndexOffset = 0)
reverse: AddIndexLet = IndexOffset - 1 (or MaxInt64 when offset = 0)
Timestamp parameters are changed from nullable (sql.NullTime with
OR-based SQL fallbacks) to always-on Go-side defaults, consistent with
the approach used by the payments query:
createdAfter → time.Unix(0, 0).UTC() (epoch, before any invoice)
createdBefore → time.Date(9999, 12, 31, …) (far future, no upper cap)
This ensures the planner always sees plain range predicates on
created_at and can use the invoices_created_at_idx index.
The SQLInvoiceQueries interface is updated to expose the five new
methods and drop FilterInvoices.
The existing FilterInvoices query uses optional parameters via the
pattern `(col >= param OR param IS NULL)` for every filter. SQLite
cannot use indexes with this pattern because the OR prevents the query
planner from determining at plan time which rows satisfy the condition,
resulting in a full table scan regardless of the available indexes
(invoices_state_idx, invoices_settle_index_idx, and the primary-key
clustered index on id).
Additionally, the conditional ORDER BY:
ORDER BY CASE WHEN reverse = FALSE ... THEN id ELSE NULL END ASC,
CASE WHEN reverse = TRUE ... THEN id ELSE NULL END DESC
prevents the planner from using the index ordering and forces an
explicit sort.
Add five focused replacements, each with a plain sargable predicate and
a direct ORDER BY so the planner can always choose an index scan:
- FetchPendingInvoices: WHERE state IN (0, 3)
- FilterInvoicesBySettleIndex: WHERE settle_index >= $1
- FilterInvoicesByAddIndex: WHERE id >= $1
- FilterInvoicesForward: WHERE id >= $1 ... ORDER BY id ASC
- FilterInvoicesReverse: WHERE id <= $1 ... ORDER BY id DESC
FilterInvoicesForward and FilterInvoicesReverse accept non-nullable
timestamp parameters (created_after, created_before) so the planner
always sees plain range predicates on created_at. Callers supply
Go-side defaults when no date filter is needed, following the same
convention already used by the payments query.
FilterInvoices is kept in this commit so all existing call sites
continue to compile. It will be removed once all callers have been
migrated.
Replace curl with wget for downloading release manifests and
signatures in verify-install.sh. wget handles redirects, retries, and
error reporting more robustly by default, which avoids silent download
failures that caused misleading "Invalid signature!" errors.
Also add error checking to all download calls so failures are reported
immediately with the URL that failed, and log which signature file and
user failed gpg verification.
When gpg --verify fails, include the signature filename, username,
and full GPG output in the error message. Previously only a generic
"Invalid signature!" was printed, making it hard to identify which
signer's signature was invalid.
Add a workflow that triggers when a release is published. It runs
verify-install.sh inside the official Docker image to validate
signatures and binary hashes. If verification fails, the release
is automatically set back to draft.
The severity bot was misclassifying CLI client code as HIGH because
filenames like cmd_walletunlocker.go matched the walletunlocker/*
auth/security keyword. Add cmd/* explicitly to the MEDIUM tier and
add a classification rule to prevent filename-based false positives.
Re-adds the old no-experimental-endorsement config option as a hidden,
deprecated alias for no-experimental-accountability. This ensures
backward compatibility for users who have the old option in their
config files after the rename it.
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.
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.
Before this change, CompletePaymentRequestsNoWait would return as
soon as the channel's NumUpdates increased by at least one. When
sending multiple payments, this meant the function could return
while some HTLCs were still in-flight and not yet committed to the
channel state.
The problem occurred when tests captured the channel state
immediately after calling this function. Even though we read the
current NumUpdates from the channel, HTLCs could still be in the
process of being committed. This led to a race where the channel
would progress to a new state after we thought we had correctly
captured it, causing tests to see unexpected commitment heights.
Fix this by waiting for all outgoing HTLCs to appear in
PendingHtlcs before returning. We count outgoing HTLCs before
sending, then wait until exactly len(paymentRequests) new HTLCs
are present. This guarantees all payments have fully completed
their commitment exchange and are locked in on both sides.
Fixes the flaky revokedCloseRetributionRemoteHodlCase test where
backups would capture state at height N+1 instead of the expected
height N.
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.
-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.
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