loop+test: add loop-in route hint regression

This commit is contained in:
Boris Nagaev 2026-04-15 00:29:08 -05:00
parent bfbc180e4b
commit e1f1077f44
No known key found for this signature in database
3 changed files with 85 additions and 6 deletions

View file

@ -10,6 +10,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/swap"
"github.com/lightninglabs/loop/test"
"github.com/lightninglabs/loop/utils"
"github.com/lightningnetwork/lnd/chainntnfs"
@ -17,6 +18,7 @@ import (
invpkg "github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/zpay32"
"github.com/stretchr/testify/require"
)
@ -124,6 +126,39 @@ func TestLoopInSuccess(t *testing.T) {
})
}
// TestLoopInSwapInvoiceRouteHintsMatchProbe asserts that explicit route hints
// are preserved on both loop-in invoices. The probe invoice already keeps the
// requested hints, while the swap invoice currently loses them via the
// lndclient AddInvoice wrapper.
func TestLoopInSwapInvoiceRouteHintsMatchProbe(t *testing.T) {
t.Parallel()
ctx := newLoopInTestContext(t)
cfg := newSwapConfig(
&ctx.lnd.LndServices, ctx.store, ctx.server, nil,
clock.NewTestClock(time.Unix(123, 0)),
)
req := testLoopInRequest
req.RouteHints = testLoopInRouteHints()
_, err := newLoopInSwap(t.Context(), cfg, 600, &req)
require.NoError(t, err)
_, swapRouteHints, _, _, err := swap.DecodeInvoice(
ctx.lnd.ChainParams, ctx.server.swapInvoice,
)
require.NoError(t, err)
_, probeRouteHints, _, _, err := swap.DecodeInvoice(
ctx.lnd.ChainParams, ctx.server.probeInvoice,
)
require.NoError(t, err)
test.RequireRouteHintsEqual(t, req.RouteHints, probeRouteHints)
test.RequireRouteHintsEqual(t, probeRouteHints, swapRouteHints)
}
func testLoopInSuccess(t *testing.T) {
defer test.Guard(t)()
@ -233,6 +268,42 @@ func testLoopInSuccess(t *testing.T) {
require.NoError(t, <-errChan)
}
// testLoopInRouteHints returns deterministic explicit route hints that can be
// encoded into loop-in invoices for regression tests.
func testLoopInRouteHints() [][]zpay32.HopHint {
_, pubKey1 := test.CreateKey(11)
_, pubKey2 := test.CreateKey(12)
_, pubKey3 := test.CreateKey(13)
return [][]zpay32.HopHint{
{
{
NodeID: pubKey1,
ChannelID: 1,
FeeBaseMSat: 10,
FeeProportionalMillionths: 20,
CLTVExpiryDelta: 30,
},
{
NodeID: pubKey2,
ChannelID: 2,
FeeBaseMSat: 11,
FeeProportionalMillionths: 21,
CLTVExpiryDelta: 31,
},
},
{
{
NodeID: pubKey3,
ChannelID: 3,
FeeBaseMSat: 12,
FeeProportionalMillionths: 22,
CLTVExpiryDelta: 32,
},
},
}
}
// TestLoopInTimeout tests scenarios where the server doesn't sweep the htlc
// and the client is forced to reclaim the funds using the timeout tx.
func TestLoopInTimeout(t *testing.T) {

View file

@ -40,9 +40,10 @@ type serverMock struct {
height int32
swapInvoice string
swapHash lntypes.Hash
prepayHash lntypes.Hash
swapInvoice string
probeInvoice string
swapHash lntypes.Hash
prepayHash lntypes.Hash
// preimagePush is a channel that preimage pushes are sent into.
preimagePush chan lntypes.Preimage
@ -157,7 +158,7 @@ func getInvoice(hash lntypes.Hash, amt btcutil.Amount, memo string) (string, err
}
func (s *serverMock) NewLoopInSwap(_ context.Context, swapHash lntypes.Hash,
amount btcutil.Amount, _, _ [33]byte, swapInvoice, _ string,
amount btcutil.Amount, _, _ [33]byte, swapInvoice, probeInvoice string,
_ *route.Vertex, _ string) (*newLoopInResponse, error) {
_, receiverKey := test.CreateKey(101)
@ -175,6 +176,7 @@ func (s *serverMock) NewLoopInSwap(_ context.Context, swapHash lntypes.Hash,
)
s.swapInvoice = swapInvoice
s.probeInvoice = probeInvoice
s.swapHash = swapHash
// Simulate the server paying the probe invoice and expect the client to

View file

@ -83,11 +83,17 @@ func (s *mockInvoices) AddHoldInvoice(ctx context.Context,
// Create and encode the payment request as a bech32 (zpay32) string.
creationDate := time.Now()
payReq, err := zpay32.NewInvoice(
s.lnd.ChainParams, *hash, creationDate,
options := []func(*zpay32.Invoice){
zpay32.Description(in.Memo),
zpay32.CLTVExpiry(in.CltvExpiry),
zpay32.Amount(in.Value),
}
for _, routeHint := range in.RouteHints {
options = append(options, zpay32.RouteHint(routeHint))
}
payReq, err := zpay32.NewInvoice(
s.lnd.ChainParams, *hash, creationDate, options...,
)
if err != nil {
return "", err