Skip to content

Commit 5bfa331

Browse files
committed
itest: enhance testEstimateRouteFee with multi-LSP scenarios
This commit enhances the integration test to validate the LSP heuristic end-to-end with real network topology and payment probing. Network topology additions: - Added Frank node as a private destination - Created multi-LSP test scenario with Bob, Eve, and Dave as LSPs New test cases: 1. "probe based estimate, public target with public hop hints" - Validates Rule 1: public invoice target routes directly - Even with public hop hints, direct routing is used - Expected: standard single-hop fees 2. "probe based estimate, multiple different public LSPs" - Validates multi-LSP worst-case selection - Frank has routes through Bob (low fee), Eve (HIGH fee), Dave (medium) - Expected: Eve's worst-case fees (most expensive) - Tests griefing protection (max 3 LSP probes)
1 parent bd57c53 commit 5bfa331

File tree

1 file changed

+87
-3
lines changed

1 file changed

+87
-3
lines changed

itest/lnd_estimate_route_fee_test.go

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,27 @@ type estimateRouteFeeTestCase struct {
5959
}
6060

6161
// testEstimateRouteFee tests the estimation of routing fees using either graph
62-
// data or sending out a probe payment.
62+
// data or sending out a probe payment. This test validates graph-based fee
63+
// estimation, probe-based fee estimation with single LSP, probe-based fee
64+
// estimation with multiple route hints to same LSP (worst-case fee selection),
65+
// probe-based fee estimation with multiple different public LSPs (worst-case
66+
// fee selection across LSPs, max 3 LSPs probed), and non-LSP probing (all
67+
// private destination hops).
6368
func testEstimateRouteFee(ht *lntest.HarnessTest) {
6469
mts := newMppTestScenario(ht)
6570

66-
// We extend the regular mpp test scenario with a new node Paula. Paula
67-
// is connected to Bob and Eve through private channels.
71+
// We extend the regular mpp test scenario with two new nodes:
72+
// - Paula: connected to Bob and Eve through private channels
73+
// - Frank: connected to Dave through a private channel
74+
//
6875
// /-------------\
6976
// _ Eve _ (private) \
7077
// / \ \
7178
// Alice -- Carol ---- Bob --------- Paula
7279
// \ / (private)
7380
// \__ Dave ____/
81+
// \
82+
// \__ Frank (private)
7483
//
7584
req := &mppOpenChannelRequest{
7685
amtAliceCarol: 200_000,
@@ -88,6 +97,7 @@ func testEstimateRouteFee(ht *lntest.HarnessTest) {
8897
probeInitiator = mts.alice
8998

9099
paula := ht.NewNode("Paula", nil)
100+
frank := ht.NewNode("Frank", nil)
91101

92102
// The channel from Bob to Paula actually doesn't have enough liquidity
93103
// to carry out the probe. We assume in normal operation that hop hints
@@ -106,6 +116,13 @@ func testEstimateRouteFee(ht *lntest.HarnessTest) {
106116
Amt: 1_000_000,
107117
})
108118

119+
// Frank is a private node connected to Dave (public LSP).
120+
ht.EnsureConnected(mts.dave, frank)
121+
ht.OpenChannel(mts.dave, frank, lntest.OpenChannelParams{
122+
Private: true,
123+
Amt: 1_000_000,
124+
})
125+
109126
bobsPrivChannels := mts.bob.RPC.ListChannels(&lnrpc.ListChannelsRequest{
110127
PrivateOnly: true,
111128
})
@@ -118,6 +135,14 @@ func testEstimateRouteFee(ht *lntest.HarnessTest) {
118135
require.Len(ht, evesPrivChannels.Channels, 1)
119136
evePaulaChanID := evesPrivChannels.Channels[0].ChanId
120137

138+
davesPrivChannels := mts.dave.RPC.ListChannels(
139+
&lnrpc.ListChannelsRequest{
140+
PrivateOnly: true,
141+
},
142+
)
143+
require.Len(ht, davesPrivChannels.Channels, 1)
144+
daveFrankChanID := davesPrivChannels.Channels[0].ChanId
145+
121146
// Let's disable the paths from Alice to Bob through Dave and Eve with
122147
// high fees. This ensures that the path estimates are based on Carol's
123148
// channel to Bob for the first set of tests.
@@ -196,6 +221,33 @@ func testEstimateRouteFee(ht *lntest.HarnessTest) {
196221
},
197222
},
198223
}
224+
225+
daveHopHint = &lnrpc.HopHint{
226+
NodeId: mts.dave.PubKeyStr,
227+
FeeBaseMsat: 3_000,
228+
FeeProportionalMillionths: 3_000,
229+
CltvExpiryDelta: 120,
230+
ChanId: daveFrankChanID,
231+
}
232+
233+
// Multiple different public LSPs (Bob, Eve, Dave).
234+
multipleLspsRouteHints = []*lnrpc.RouteHint{
235+
{
236+
HopHints: []*lnrpc.HopHint{
237+
bobHopHint,
238+
},
239+
},
240+
{
241+
HopHints: []*lnrpc.HopHint{
242+
eveHopHint,
243+
},
244+
},
245+
{
246+
HopHints: []*lnrpc.HopHint{
247+
daveHopHint,
248+
},
249+
},
250+
}
199251
)
200252

201253
defaultTimelock := int64(chainreg.DefaultBitcoinTimeLockDelta)
@@ -231,6 +283,11 @@ func testEstimateRouteFee(ht *lntest.HarnessTest) {
231283
feeACEP := feeEP + feeCE
232284
deltaACEP := deltaCE + deltaEP
233285

286+
// For multiple LSPs test, the worst-case (most expensive) route should
287+
// be selected. Eve has the highest fees.
288+
worstCaseFeeMultipleLsps := feeACEP
289+
worstCaseDeltaMultipleLsps := deltaACEP
290+
234291
initialBlockHeight := int64(mts.alice.RPC.GetInfo().BlockHeight)
235292

236293
// Locktime is always composed of the initial block height and the
@@ -271,6 +328,19 @@ func testEstimateRouteFee(ht *lntest.HarnessTest) {
271328
expectedCltvDelta: locktime + deltaCB,
272329
expectedFailureReason: failureReasonNone,
273330
},
331+
// Rule 1: Invoice target is public (Bob), even with public
332+
// destination hop hints. Should route directly to Bob, NOT
333+
// treat as LSP.
334+
{
335+
name: "probe based estimate, public " +
336+
"target with public hop hints",
337+
probing: true,
338+
destination: mts.bob,
339+
routeHints: singleRouteHint,
340+
expectedRoutingFeesMsat: feeStandardSingleHop,
341+
expectedCltvDelta: locktime + deltaCB,
342+
expectedFailureReason: failureReasonNone,
343+
},
274344
// We expect the previous probing results adjusted by Paula's
275345
// hop data.
276346
{
@@ -340,6 +410,20 @@ func testEstimateRouteFee(ht *lntest.HarnessTest) {
340410
expectedCltvDelta: 0,
341411
expectedFailureReason: failureReasonNoRoute,
342412
},
413+
// Test multiple different public LSPs. The worst-case (most
414+
// expensive) route should be returned. Eve has the highest
415+
// fees.
416+
{
417+
name: "probe based estimate, " +
418+
"multiple different public LSPs",
419+
probing: true,
420+
destination: frank,
421+
routeHints: multipleLspsRouteHints,
422+
expectedRoutingFeesMsat: worstCaseFeeMultipleLsps,
423+
expectedCltvDelta: locktime +
424+
worstCaseDeltaMultipleLsps,
425+
expectedFailureReason: failureReasonNone,
426+
},
343427
}
344428

345429
for _, testCase := range testCases {

0 commit comments

Comments
 (0)