The GetInvoice query used an OR IS NULL pattern for each filter
parameter:
WHERE (i.hash = $1 OR $1 IS NULL)
AND (i.payment_addr = $2 OR $2 IS NULL)
SQLite's query planner decides on an execution plan at prepare time,
before seeing any parameter values. Because either condition can be
trivially true when its parameter is NULL, the planner conservatively
falls back to a full table scan rather than using the unique indexes on
hash and payment_addr. This caused every invoice lookup and update on
the hot path (HTLC settlement) to scan the entire invoices table.
Replace the single catch-all query with three dedicated queries, each
using a direct equality on a uniquely constrained column:
- GetInvoiceByHash: WHERE hash = $1
- GetInvoiceByAddr: WHERE payment_addr = $1 (new, covers AMP path)
- GetInvoiceBySetID: existing, unchanged
Update getInvoiceByRef to route to the appropriate query based on which
fields are present in the InvoiceRef. When both hash and payment address
are provided, we look up by hash and then verify the returned invoice's
payment address matches.
GetInvoice is now unused and removed from the SQLInvoiceQueries
interface.
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.
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.
Increase the test timeout from 10s to 60s to accommodate slow
Postgres database setup and migrations when running tests in
parallel. This prevents false-positive test failures on slower
CI runners.
In this commit, we complete the migration to lnutils.SpewLogClosure for
the remaining application-layer components. This includes the autopilot
channel management system, the funding manager, the RPC server, and the
invoice storage subsystem.
Use the new feature of Go 1.24, fix linter warnings.
This change was produced by:
- running golangci-lint run --fix
- sed 's/context.Background/t.Context/' -i `git grep -l context.Background | grep test.go`
- manually fixing broken tests
- itest, lntest: use ht.Context() where ht or hn is available
- in HarnessNode.Stop() we keep using context.Background(), because it is
called from a cleanup handler in which t.Context() is canceled already.
The BOLT spec was updated and now requires the payment address for
the writer and reader. We increase the test coverage for payments
setting a wrong payment address.
Add a re-usable implementation of the sqldb.TxOptions interface and make
use of this in the various spots (invoices, batch and graph/db) where we
had previously defined individual implementations that were all doing
the same thing.
We now cancel all HTLCs of an AMP invoice as soon as it expires.
Otherwise because we mark the invoice as cancelled we would not
allow accepted HTLCs to be resolved via the invoiceEventLoop.
We always fetch the HTLCs for the specific setID, so there is no
need to keep this code. In earlier versions we would call the
UpdateInvoice method with `nil` for the setID therefore we had
to lookup the AMPState. However this was error prune because in
case one partial payment times-out the AMPState would change to
cancelled and that could lead to not resolve HTLCs.
We introduce a new specific fail resolution error when the
external HTLC interceptor denies the incoming HTLC. Moreover
we introduce a new traffic shaper method which moves the
implementation of asset HTLC to the external layers.
Moreover itests are adopted to reflect this new change.
We make sure that HTLCs which have already been decided upon
are resolved before before allowing the external interceptor to
potentially cancel them back. This makes the implementation for
the external HTLC interceptor more streamlined.
Previously, we applied replacements to our schema definitions
to make them compatible with both SQLite and Postgres backends,
as the files were not fully compatible with either.
With this change, the only replacement required for SQLite has
been moved to the generator script. This adjustment ensures
compatibility by enabling auto-incrementing primary keys that
are treated as 64-bit integers by sqlc.
Previously we'd recalculate the paid amount by summing amounts of
settled HTLCs. This approach while correct would stop the SQL migration
process as some KV invoices may have incorrectly stored paid amounts.
The current sqlc GetInvoice query experiences incremental slowdowns during
the migration of large invoice databases, primarily due to its complex
predicate set. For this specific use case, a streamlined GetInvoiceByHash
function provides a more efficient solution, maintaining near-constant
lookup times even with extensive table sizes.
Certain invoices may not have a deterministic payment hash. For such
invoices we still store the payment hashes in our KV database, but we do
not have a sufficient index to retrieve them. This PR adds such index to
the SQL database that will be used during migration to retrieve payment
hashes.
When calling `NotifyExitHopHtlc` it is allowed to pass a chan to
subscribe to the HTLC's resolution when it's settled. However, this
method will also return immediately if there's already a resolution,
which means it behaves like a notifier and a getter. If the caller
decides to only use the getter to do a non-blocking lookup, it can pass
a nil subscriber chan to bypass the notification.
Find and replace all nolint instances refering to the `lll` linter and
replace with `ll` which is the name of our custom version of the `lll`
linter which can be used to ignore log lines during linting.
The next commit will do the configuration of the custom linter and
disable the default one.
All the structs defined in the `channeldb/models` package are graph
related. So once we move all the graph CRUD code to the graph package,
it makes sense to have the schema structs there too. So this just moves
the `models` package over to `graph/db/models`.
During testing of this PR I figured out the the htlc interceptor
was not shutdown and started properly in terms of closing the
quit channel but also in terms of the relevant logs.
This commit updates the invoice registry to utilize the settlement
interceptor during the invoice settlement routine. It allows the
interceptor to capture the invoice, providing interception clients an
opportunity to determine the settlement outcome.