From d324b4bfd89d1f6978bc007cf884050e49617abf Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Sat, 20 Jun 2026 16:24:35 -0500 Subject: [PATCH] loop: omit payment hops in cost migration The cost cleanup migration pages through LND payments only to build a payment-hash to fee map. It does not inspect HTLC attempts, routes, or per-hop data; pagination still uses the top-level index offsets returned by ListPayments. Setting OmitHops is safe for this migration because LND only strips hop-level route data from HTLC attempts, while preserving the top-level payment fields the migration reads: hash, fee, and response offsets. This reduces response size and query cost for nodes with many or large MPP payments without changing the calculated swap costs. The migration test records the mocked ListPayments requests and asserts that OmitHops is set. --- cost_migration.go | 1 + cost_migration_test.go | 5 +++++ test/lightning_client_mock.go | 8 +++++++- test/lnd_services_mock.go | 27 +++++++++++++++++++++------ 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/cost_migration.go b/cost_migration.go index 14fa2c87..d99ef58b 100644 --- a/cost_migration.go +++ b/cost_migration.go @@ -142,6 +142,7 @@ func MigrateLoopOutCosts(ctx context.Context, lnd lndclient.LndServices, ctx, lndclient.ListPaymentsRequest{ Offset: offset, MaxPayments: uint64(paymentBatchSize), + OmitHops: true, }, ) if err != nil { diff --git a/cost_migration_test.go b/cost_migration_test.go index 082895f2..abdc80d3 100644 --- a/cost_migration_test.go +++ b/cost_migration_test.go @@ -163,6 +163,11 @@ func TestCostMigration(t *testing.T) { // Now we can run the migration. err = MigrateLoopOutCosts(context.Background(), lnd.LndServices, 1, store) require.NoError(t, err) + listPaymentsRequests := lnd.ListPaymentsRequestsSnapshot() + require.NotEmpty(t, listPaymentsRequests) + for _, req := range listPaymentsRequests { + require.True(t, req.OmitHops) + } // Finally check that the swap cost has been updated correctly. swap, err := store.FetchLoopOutSwap( diff --git a/test/lightning_client_mock.go b/test/lightning_client_mock.go index 9a9b9047..27f3fada 100644 --- a/test/lightning_client_mock.go +++ b/test/lightning_client_mock.go @@ -266,6 +266,11 @@ func (h *mockLightningClient) ListPayments(_ context.Context, req lndclient.ListPaymentsRequest) (*lndclient.ListPaymentsResponse, error) { + h.lnd.lock.Lock() + defer h.lnd.lock.Unlock() + + h.lnd.ListPaymentsRequests = append(h.lnd.ListPaymentsRequests, req) + if req.Offset >= uint64(len(h.lnd.Payments)) { return &lndclient.ListPaymentsResponse{}, nil } @@ -273,7 +278,8 @@ func (h *mockLightningClient) ListPayments(_ context.Context, lastIndexOffset := req.Offset + req.MaxPayments lastIndexOffset = min(lastIndexOffset, uint64(len(h.lnd.Payments))) - result := h.lnd.Payments[req.Offset:lastIndexOffset] + result := make([]lndclient.Payment, lastIndexOffset-req.Offset) + copy(result, h.lnd.Payments[req.Offset:lastIndexOffset]) return &lndclient.ListPaymentsResponse{ Payments: result, diff --git a/test/lnd_services_mock.go b/test/lnd_services_mock.go index 164bea18..63aa34c2 100644 --- a/test/lnd_services_mock.go +++ b/test/lnd_services_mock.go @@ -162,12 +162,13 @@ type LndMockServices struct { // keyed by hash string. Invoices map[lntypes.Hash]*lndclient.Invoice - Channels []lndclient.ChannelInfo - ChannelEdges map[uint64]*lndclient.ChannelEdge - ClosedChannels []lndclient.ClosedChannel - ForwardingEvents []lndclient.ForwardingEvent - Payments []lndclient.Payment - MissionControlState []lndclient.MissionControlEntry + Channels []lndclient.ChannelInfo + ChannelEdges map[uint64]*lndclient.ChannelEdge + ClosedChannels []lndclient.ClosedChannel + ForwardingEvents []lndclient.ForwardingEvent + Payments []lndclient.Payment + ListPaymentsRequests []lndclient.ListPaymentsRequest + MissionControlState []lndclient.MissionControlEntry WaitForFinished func() @@ -185,6 +186,20 @@ func (s *LndMockServices) EpochSubscribers() int32 { return int32(len(s.blockHeightListeners)) } +// ListPaymentsRequestsSnapshot returns a copy of all ListPayments requests +// recorded by the mock. +func (s *LndMockServices) ListPaymentsRequestsSnapshot() []lndclient.ListPaymentsRequest { + s.lock.Lock() + defer s.lock.Unlock() + + requests := make( + []lndclient.ListPaymentsRequest, len(s.ListPaymentsRequests), + ) + copy(requests, s.ListPaymentsRequests) + + return requests +} + // NotifyHeight notifies a new block height. func (s *LndMockServices) NotifyHeight(height int32) error { s.lock.Lock()