Skip to content

Commit 7e0b198

Browse files
authored
Merge pull request #1031 from GeorgeTsagk/htlcview-itest
Add new custom channels itest liquidity edge case for safe HTLC failure
2 parents f664d75 + 9c16e89 commit 7e0b198

File tree

3 files changed

+326
-38
lines changed

3 files changed

+326
-38
lines changed

itest/assets_test.go

+191-8
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,7 @@ func sendAssetKeySendPayment(t *testing.T, src, dst *HarnessNode, amt uint64,
10331033
DestCustomRecords: customRecords,
10341034
PaymentHash: hash[:],
10351035
TimeoutSeconds: int32(PaymentTimeout.Seconds()),
1036+
MaxParts: cfg.maxShards,
10361037
}
10371038

10381039
request := &tchrpc.SendPaymentRequest{
@@ -1090,7 +1091,7 @@ func sendKeySendPayment(t *testing.T, src, dst *HarnessNode,
10901091
stream, err := src.RouterClient.SendPaymentV2(ctxt, req)
10911092
require.NoError(t, err)
10921093

1093-
result, err := getPaymentResult(stream)
1094+
result, err := getFinalPaymentResult(stream)
10941095
require.NoError(t, err)
10951096
require.Equal(t, lnrpc.Payment_SUCCEEDED, result.Status)
10961097
}
@@ -1135,6 +1136,12 @@ func createAndPayNormalInvoice(t *testing.T, src, rfqPeer, dst *HarnessNode,
11351136
func payInvoiceWithSatoshi(t *testing.T, payer *HarnessNode,
11361137
invoice *lnrpc.AddInvoiceResponse, opts ...payOpt) {
11371138

1139+
payPayReqWithSatoshi(t, payer, invoice.PaymentRequest, opts...)
1140+
}
1141+
1142+
func payPayReqWithSatoshi(t *testing.T, payer *HarnessNode, payReq string,
1143+
opts ...payOpt) {
1144+
11381145
cfg := defaultPayConfig()
11391146
for _, opt := range opts {
11401147
opt(cfg)
@@ -1145,15 +1152,30 @@ func payInvoiceWithSatoshi(t *testing.T, payer *HarnessNode,
11451152
defer cancel()
11461153

11471154
sendReq := &routerrpc.SendPaymentRequest{
1148-
PaymentRequest: invoice.PaymentRequest,
1149-
TimeoutSeconds: int32(PaymentTimeout.Seconds()),
1150-
MaxShardSizeMsat: 80_000_000,
1151-
FeeLimitMsat: 1_000_000,
1155+
PaymentRequest: payReq,
1156+
TimeoutSeconds: int32(PaymentTimeout.Seconds()),
1157+
FeeLimitMsat: 1_000_000,
1158+
MaxParts: cfg.maxShards,
1159+
}
1160+
1161+
if cfg.smallShards {
1162+
sendReq.MaxShardSizeMsat = 80_000_000
11521163
}
1164+
11531165
stream, err := payer.RouterClient.SendPaymentV2(ctxt, sendReq)
11541166
require.NoError(t, err)
11551167

1156-
result, err := getPaymentResult(stream)
1168+
if cfg.payStatus == lnrpc.Payment_IN_FLIGHT {
1169+
t.Logf("Waiting for initial stream response...")
1170+
result, err := stream.Recv()
1171+
require.NoError(t, err)
1172+
1173+
require.Equal(t, cfg.payStatus, result.Status)
1174+
1175+
return
1176+
}
1177+
1178+
result, err := getFinalPaymentResult(stream)
11571179
if cfg.errSubStr != "" {
11581180
require.ErrorContains(t, err, cfg.errSubStr)
11591181
} else {
@@ -1212,6 +1234,7 @@ func payInvoiceWithSatoshiLastHop(t *testing.T, payer *HarnessNode,
12121234

12131235
type payConfig struct {
12141236
smallShards bool
1237+
maxShards uint32
12151238
errSubStr string
12161239
allowOverpay bool
12171240
feeLimit lnwire.MilliSatoshi
@@ -1240,6 +1263,12 @@ func withSmallShards() payOpt {
12401263
}
12411264
}
12421265

1266+
func withMaxShards(maxShards uint32) payOpt {
1267+
return func(c *payConfig) {
1268+
c.maxShards = maxShards
1269+
}
1270+
}
1271+
12431272
func withPayErrSubStr(errSubStr string) payOpt {
12441273
return func(c *payConfig) {
12451274
c.errSubStr = errSubStr
@@ -1310,6 +1339,7 @@ func payInvoiceWithAssets(t *testing.T, payer, rfqPeer *HarnessNode,
13101339
TimeoutSeconds: int32(PaymentTimeout.Seconds()),
13111340
FeeLimitMsat: int64(cfg.feeLimit),
13121341
DestCustomRecords: cfg.destCustomRecords,
1342+
MaxParts: cfg.maxShards,
13131343
}
13141344

13151345
if cfg.smallShards {
@@ -1394,8 +1424,9 @@ func payInvoiceWithAssets(t *testing.T, payer, rfqPeer *HarnessNode,
13941424
}
13951425

13961426
type invoiceConfig struct {
1397-
errSubStr string
1398-
groupKey []byte
1427+
errSubStr string
1428+
groupKey []byte
1429+
routeHints []*lnrpc.RouteHint
13991430
}
14001431

14011432
func defaultInvoiceConfig() *invoiceConfig {
@@ -2292,6 +2323,158 @@ func macFromBytes(macBytes []byte) (grpc.DialOption, error) {
22922323
return grpc.WithPerRPCCredentials(cred), nil
22932324
}
22942325

2326+
func assertMinNumHtlcs(t *testing.T, node *HarnessNode, expected int) {
2327+
t.Helper()
2328+
2329+
ctxb := context.Background()
2330+
2331+
err := wait.NoError(func() error {
2332+
listChansRequest := &lnrpc.ListChannelsRequest{}
2333+
listChansResp, err := node.ListChannels(ctxb, listChansRequest)
2334+
if err != nil {
2335+
return err
2336+
}
2337+
2338+
var numHtlcs int
2339+
for _, channel := range listChansResp.Channels {
2340+
numHtlcs += len(channel.PendingHtlcs)
2341+
}
2342+
2343+
if numHtlcs < expected {
2344+
return fmt.Errorf("expected %v HTLCs, got %v, %v",
2345+
expected, numHtlcs,
2346+
toProtoJSON(t, listChansResp))
2347+
}
2348+
2349+
return nil
2350+
}, defaultTimeout)
2351+
require.NoError(t, err)
2352+
}
2353+
2354+
type subscribeEventsClient = routerrpc.Router_SubscribeHtlcEventsClient
2355+
2356+
type htlcEventConfig struct {
2357+
timeout time.Duration
2358+
numEvents int
2359+
withLinkFailure bool
2360+
withForwardFailure bool
2361+
withFailureDetail routerrpc.FailureDetail
2362+
}
2363+
2364+
func defaultHtlcEventConfig() *htlcEventConfig {
2365+
return &htlcEventConfig{
2366+
timeout: defaultTimeout,
2367+
}
2368+
}
2369+
2370+
type htlcEventOpt func(*htlcEventConfig)
2371+
2372+
func withTimeout(timeout time.Duration) htlcEventOpt {
2373+
return func(config *htlcEventConfig) {
2374+
config.timeout = timeout
2375+
}
2376+
}
2377+
2378+
func withNumEvents(numEvents int) htlcEventOpt {
2379+
return func(config *htlcEventConfig) {
2380+
config.numEvents = numEvents
2381+
}
2382+
}
2383+
2384+
func withLinkFailure(detail routerrpc.FailureDetail) htlcEventOpt {
2385+
return func(config *htlcEventConfig) {
2386+
config.withLinkFailure = true
2387+
config.withFailureDetail = detail
2388+
}
2389+
}
2390+
2391+
func withForwardFailure() htlcEventOpt {
2392+
return func(config *htlcEventConfig) {
2393+
config.withForwardFailure = true
2394+
}
2395+
}
2396+
2397+
func assertHtlcEvents(t *testing.T, c subscribeEventsClient,
2398+
opts ...htlcEventOpt) {
2399+
2400+
t.Helper()
2401+
2402+
cfg := defaultHtlcEventConfig()
2403+
for _, opt := range opts {
2404+
opt(cfg)
2405+
}
2406+
2407+
timeout := time.After(cfg.timeout)
2408+
events := make(chan *routerrpc.HtlcEvent)
2409+
2410+
go func() {
2411+
defer close(events)
2412+
2413+
for {
2414+
evt, err := c.Recv()
2415+
if err != nil {
2416+
t.Logf("Received HTLC event error: %v", err)
2417+
return
2418+
}
2419+
2420+
select {
2421+
case events <- evt:
2422+
case <-timeout:
2423+
t.Logf("Htlc event receive timeout")
2424+
return
2425+
}
2426+
}
2427+
}()
2428+
2429+
var numEvents int
2430+
for {
2431+
type (
2432+
linkFailEvent = *routerrpc.HtlcEvent_LinkFailEvent
2433+
forwardFailEvent = *routerrpc.HtlcEvent_ForwardFailEvent
2434+
)
2435+
2436+
select {
2437+
case evt, ok := <-events:
2438+
if !ok {
2439+
t.Fatalf("Htlc event stream closed")
2440+
return
2441+
}
2442+
2443+
if cfg.withLinkFailure {
2444+
linkEvent, ok := evt.Event.(linkFailEvent)
2445+
if !ok {
2446+
// We only count link failure events.
2447+
continue
2448+
}
2449+
2450+
if linkEvent.LinkFailEvent.FailureDetail !=
2451+
cfg.withFailureDetail {
2452+
2453+
continue
2454+
}
2455+
}
2456+
2457+
if cfg.withForwardFailure {
2458+
_, ok := evt.Event.(forwardFailEvent)
2459+
if !ok {
2460+
// We only count link failure events.
2461+
continue
2462+
}
2463+
}
2464+
2465+
numEvents++
2466+
2467+
if numEvents == cfg.numEvents {
2468+
return
2469+
}
2470+
2471+
case <-timeout:
2472+
t.Fatalf("Htlc event receive timeout")
2473+
return
2474+
}
2475+
}
2476+
}
2477+
22952478
func assertNumHtlcs(t *testing.T, node *HarnessNode, expected int) {
22962479
t.Helper()
22972480

itest/litd_accounts_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -416,12 +416,12 @@ func payNode(invoiceCtx, paymentCtx context.Context, t *harnessTest,
416416
stream, err := from.SendPaymentV2(paymentCtx, sendReq)
417417
require.NoError(t.t, err)
418418

419-
result, err := getPaymentResult(stream)
419+
result, err := getFinalPaymentResult(stream)
420420
require.NoError(t.t, err)
421421
require.Equal(t.t, result.Status, lnrpc.Payment_SUCCEEDED)
422422
}
423423

424-
func getPaymentResult(stream routerrpc.Router_SendPaymentV2Client) (
424+
func getFinalPaymentResult(stream routerrpc.Router_SendPaymentV2Client) (
425425
*lnrpc.Payment, error) {
426426

427427
for {

0 commit comments

Comments
 (0)