Skip to content

Commit 3e6cb16

Browse files
committed
staticaddr/loopin: add route hint regression test
1 parent 2032d79 commit 3e6cb16

1 file changed

Lines changed: 148 additions & 0 deletions

File tree

staticaddr/loopin/actions_test.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,22 @@ import (
77
"time"
88

99
"github.com/btcsuite/btcd/btcec/v2"
10+
"github.com/btcsuite/btcd/chaincfg/chainhash"
11+
"github.com/btcsuite/btcd/wire"
1012
"github.com/lightninglabs/lndclient"
1113
"github.com/lightninglabs/loop/fsm"
1214
"github.com/lightninglabs/loop/staticaddr/address"
1315
"github.com/lightninglabs/loop/staticaddr/deposit"
1416
"github.com/lightninglabs/loop/staticaddr/script"
1517
"github.com/lightninglabs/loop/staticaddr/version"
18+
"github.com/lightninglabs/loop/swap"
19+
"github.com/lightninglabs/loop/swapserverrpc"
1620
"github.com/lightninglabs/loop/test"
1721
"github.com/lightningnetwork/lnd/invoices"
1822
"github.com/lightningnetwork/lnd/lntypes"
23+
"github.com/lightningnetwork/lnd/zpay32"
1924
"github.com/stretchr/testify/require"
25+
"google.golang.org/grpc"
2026
)
2127

2228
// TestMonitorInvoiceAndHtlcTxReRegistersOnConfErr ensures that an error from
@@ -123,6 +129,148 @@ func TestMonitorInvoiceAndHtlcTxReRegistersOnConfErr(t *testing.T) {
123129
}
124130
}
125131

132+
// TestInitHtlcActionPreservesRouteHints asserts that static-address loop-in
133+
// propagates explicit route hints into the encoded swap invoice sent to the
134+
// server. This currently fails because lndclient.AddInvoice drops route hints.
135+
func TestInitHtlcActionPreservesRouteHints(t *testing.T) {
136+
t.Parallel()
137+
138+
mockLnd := test.NewMockLnd()
139+
_, serverKey := test.CreateKey(21)
140+
141+
server := &mockStaticAddressServer{
142+
response: testStaticAddressLoopInResponse(
143+
serverKey.SerializeCompressed(),
144+
),
145+
}
146+
147+
dep := &deposit.Deposit{
148+
OutPoint: wire.OutPoint{
149+
Hash: chainhash.Hash{1},
150+
Index: 0,
151+
},
152+
Value: 500_000,
153+
}
154+
155+
loopIn := &StaticAddressLoopIn{
156+
Deposits: []*deposit.Deposit{dep},
157+
DepositOutpoints: []string{dep.OutPoint.String()},
158+
SelectedAmount: dep.Value,
159+
QuotedSwapFee: 1_000,
160+
RouteHints: testStaticAddressRouteHints(),
161+
InitiationHeight: uint32(mockLnd.Height),
162+
InitiationTime: time.Now(),
163+
PaymentTimeoutSeconds: 3_600,
164+
}
165+
166+
f := &FSM{
167+
StateMachine: &fsm.StateMachine{},
168+
cfg: &Config{
169+
Server: server,
170+
DepositManager: &noopDepositManager{},
171+
LndClient: mockLnd.Client,
172+
WalletKit: mockLnd.WalletKit,
173+
ChainParams: mockLnd.ChainParams,
174+
Store: &mockStore{},
175+
ValidateLoopInContract: testValidateLoopInContract,
176+
MaxStaticAddrHtlcFeePercentage: 1,
177+
MaxStaticAddrHtlcBackupFeePercentage: 1,
178+
},
179+
loopIn: loopIn,
180+
}
181+
182+
event := f.InitHtlcAction(t.Context(), nil)
183+
require.Equal(t, OnHtlcInitiated, event)
184+
require.Nil(t, f.LastActionError)
185+
require.NotNil(t, server.request)
186+
187+
_, routeHints, _, _, err := swap.DecodeInvoice(
188+
mockLnd.ChainParams, server.request.SwapInvoice,
189+
)
190+
require.NoError(t, err)
191+
192+
test.RequireRouteHintsEqual(t, loopIn.RouteHints, routeHints)
193+
}
194+
195+
// mockStaticAddressServer captures static-address loop-in requests in tests.
196+
type mockStaticAddressServer struct {
197+
swapserverrpc.StaticAddressServerClient
198+
199+
request *swapserverrpc.ServerStaticAddressLoopInRequest
200+
response *swapserverrpc.ServerStaticAddressLoopInResponse
201+
}
202+
203+
// ServerStaticAddressLoopIn records the request and returns the prepared
204+
// response.
205+
func (m *mockStaticAddressServer) ServerStaticAddressLoopIn(
206+
_ context.Context, in *swapserverrpc.ServerStaticAddressLoopInRequest,
207+
_ ...grpc.CallOption) (*swapserverrpc.ServerStaticAddressLoopInResponse,
208+
error) {
209+
210+
m.request = in
211+
212+
return m.response, nil
213+
}
214+
215+
// testStaticAddressLoopInResponse returns a minimal successful server response
216+
// for InitHtlcAction tests.
217+
func testStaticAddressLoopInResponse(
218+
serverPubKey []byte) *swapserverrpc.ServerStaticAddressLoopInResponse {
219+
220+
signingInfo := &swapserverrpc.ServerHtlcSigningInfo{
221+
FeeRate: 1,
222+
}
223+
224+
return &swapserverrpc.ServerStaticAddressLoopInResponse{
225+
HtlcServerPubKey: serverPubKey,
226+
HtlcExpiry: 1_000,
227+
StandardHtlcInfo: signingInfo,
228+
HighFeeHtlcInfo: signingInfo,
229+
ExtremeFeeHtlcInfo: signingInfo,
230+
}
231+
}
232+
233+
// testStaticAddressRouteHints returns deterministic route hints for static
234+
// loop-in invoice regression tests.
235+
func testStaticAddressRouteHints() [][]zpay32.HopHint {
236+
_, pubKey1 := test.CreateKey(31)
237+
_, pubKey2 := test.CreateKey(32)
238+
_, pubKey3 := test.CreateKey(33)
239+
240+
return [][]zpay32.HopHint{
241+
{
242+
{
243+
NodeID: pubKey1,
244+
ChannelID: 11,
245+
FeeBaseMSat: 101,
246+
FeeProportionalMillionths: 201,
247+
CLTVExpiryDelta: 31,
248+
},
249+
{
250+
NodeID: pubKey2,
251+
ChannelID: 12,
252+
FeeBaseMSat: 102,
253+
FeeProportionalMillionths: 202,
254+
CLTVExpiryDelta: 32,
255+
},
256+
},
257+
{
258+
{
259+
NodeID: pubKey3,
260+
ChannelID: 13,
261+
FeeBaseMSat: 103,
262+
FeeProportionalMillionths: 203,
263+
CLTVExpiryDelta: 33,
264+
},
265+
},
266+
}
267+
}
268+
269+
// testValidateLoopInContract accepts all server contract parameters in tests.
270+
func testValidateLoopInContract(_ int32, _ int32) error {
271+
return nil
272+
}
273+
126274
// mockAddressManager is a minimal AddressManager implementation used by the
127275
// test FSM setup.
128276
type mockAddressManager struct {

0 commit comments

Comments
 (0)