1
1
package itest
2
2
3
3
import (
4
+ "bytes"
4
5
"context"
5
6
"crypto/sha256"
6
7
"encoding/hex"
8
+ "time"
7
9
8
10
"github.com/btcsuite/btcd/btcec/v2"
9
11
"github.com/btcsuite/btcd/btcutil"
@@ -323,6 +325,10 @@ type blindedForwardTest struct {
323
325
dave * node.HarnessNode
324
326
channels []* lnrpc.ChannelPoint
325
327
328
+ carolInterceptor routerrpc.Router_HtlcInterceptorClient
329
+
330
+ preimage [33 ]byte
331
+
326
332
// cancel will cancel the test's top level context.
327
333
cancel func ()
328
334
}
@@ -333,16 +339,28 @@ func newBlindedForwardTest(ht *lntest.HarnessTest) (context.Context,
333
339
ctx , cancel := context .WithCancel (context .Background ())
334
340
335
341
return ctx , & blindedForwardTest {
336
- ht : ht ,
337
- cancel : cancel ,
342
+ ht : ht ,
343
+ cancel : cancel ,
344
+ preimage : [33 ]byte {1 , 2 , 3 },
338
345
}
339
346
}
340
347
341
348
// setup spins up additional nodes needed for our test and creates a four hop
342
349
// network for testing blinded forwarding and returns a blinded route from
343
- // Bob -> Carol -> Dave, with Bob acting as the introduction point.
344
- func (b * blindedForwardTest ) setup () * routing.BlindedPayment {
345
- b .carol = b .ht .NewNode ("Carol" , nil )
350
+ // Bob -> Carol -> Dave, with Bob acting as the introduction point and an
351
+ // interceptor on Carol's node to manage HTLCs (as Dave does not yet support
352
+ // receiving).
353
+ func (b * blindedForwardTest ) setup (
354
+ ctx context.Context ) * routing.BlindedPayment {
355
+
356
+ b .carol = b .ht .NewNode ("Carol" , []string {
357
+ "requireinterceptor" ,
358
+ })
359
+
360
+ var err error
361
+ b .carolInterceptor , err = b .carol .RPC .Router .HtlcInterceptor (ctx )
362
+ require .NoError (b .ht , err , "interceptor" )
363
+
346
364
b .dave = b .ht .NewNode ("Dave" , nil )
347
365
348
366
b .channels = setupFourHopNetwork (b .ht , b .carol , b .dave )
@@ -431,6 +449,77 @@ func (b *blindedForwardTest) createRouteToBlinded(paymentAmt int64,
431
449
return resp .Routes [0 ]
432
450
}
433
451
452
+ // sendBlindedPayment dispatches a payment to the route provided. The streaming
453
+ // client for the send is returned with a cancel function that can be used to
454
+ // terminate the stream.
455
+ func (b * blindedForwardTest ) sendBlindedPayment (ctx context.Context ,
456
+ route * lnrpc.Route ) (lnrpc.Lightning_SendToRouteClient , func ()) {
457
+
458
+ hash := sha256 .Sum256 (b .preimage [:])
459
+
460
+ ctxt , cancel := context .WithCancel (ctx )
461
+ sendReq := & lnrpc.SendToRouteRequest {
462
+ PaymentHash : hash [:],
463
+ Route : route ,
464
+ }
465
+
466
+ sendClient , err := b .ht .Alice .RPC .LN .SendToRoute (ctxt )
467
+ require .NoError (b .ht , err , "send to route client" )
468
+
469
+ err = sendClient .SendMsg (sendReq )
470
+ require .NoError (b .ht , err , "send to route request" )
471
+
472
+ return sendClient , cancel
473
+ }
474
+
475
+ // interceptFinalHop launches a goroutine to intercept Carol's htlcs and
476
+ // returns a closure that can be used to settle intercepted htlcs.
477
+ func (b * blindedForwardTest ) interceptFinalHop () func () {
478
+ hash := sha256 .Sum256 (b .preimage [:])
479
+ htlcReceived := make (chan * routerrpc.ForwardHtlcInterceptRequest )
480
+
481
+ // Launch a goroutine which will receive from the interceptor and pipe
482
+ // it into our request channel.
483
+ go func () {
484
+ forward , err := b .carolInterceptor .Recv ()
485
+ if err != nil {
486
+ b .ht .Fatalf ("intercept receive failed: %v" , err )
487
+ }
488
+
489
+ if ! bytes .Equal (forward .PaymentHash , hash [:]) {
490
+ b .ht .Fatalf ("unexpected payment hash: %v" , hash )
491
+ }
492
+
493
+ select {
494
+ case htlcReceived <- forward :
495
+
496
+ case <- time .After (lntest .DefaultTimeout ):
497
+ b .ht .Fatal ("timeout waiting to send intercepted htlc" )
498
+ }
499
+ }()
500
+
501
+ // Create a closure that will wait for the intercept request and settle
502
+ // it with our preimage.
503
+ settleHTLC := func () {
504
+ select {
505
+ case forward := <- htlcReceived :
506
+ action := routerrpc .ResolveHoldForwardAction_SETTLE
507
+ resp := & routerrpc.ForwardHtlcInterceptResponse {
508
+ IncomingCircuitKey : forward .IncomingCircuitKey ,
509
+ Action : action ,
510
+ Preimage : b .preimage [:],
511
+ }
512
+
513
+ require .NoError (b .ht , b .carolInterceptor .Send (resp ))
514
+
515
+ case <- time .After (lntest .DefaultTimeout ):
516
+ b .ht .Fatal ("timeout waiting for htlc intercept" )
517
+ }
518
+ }
519
+
520
+ return settleHTLC
521
+ }
522
+
434
523
// setupFourHopNetwork creates a network with the following topology and
435
524
// liquidity:
436
525
// Alice (100k)----- Bob (100k) ----- Carol (100k) ----- Dave
@@ -615,9 +704,37 @@ func getForwardingEdge(ht *lntest.HarnessTest,
615
704
// testForwardBlindedRoute tests lnd's ability to forward payments in a blinded
616
705
// route.
617
706
func testForwardBlindedRoute (ht * lntest.HarnessTest ) {
618
- _ , testCase := newBlindedForwardTest (ht )
707
+ ctx , testCase := newBlindedForwardTest (ht )
619
708
defer testCase .cleanup ()
620
709
621
- route := testCase .setup ()
622
- testCase .createRouteToBlinded (100_000 , route )
710
+ route := testCase .setup (ctx )
711
+ blindedRoute := testCase .createRouteToBlinded (100_000 , route )
712
+
713
+ // Receiving via blinded routes is not yet supported, so Dave won't be
714
+ // able to process the payment.
715
+ //
716
+ // We have an interceptor at our disposal that will catch htlcs as they
717
+ // are forwarded (ie, it won't intercept a HTLC that dave is receiving,
718
+ // since no forwarding occurs). We initiate this interceptor with
719
+ // Carol, so that we can catch it and settle on the outgoing link to
720
+ // Dave. Once we hit the outgoing link, we know that we successfully
721
+ // parsed the htlc, so this is an acceptable compromise.
722
+ // Assert that our interceptor has exited without an error.
723
+ settleHTLC := testCase .interceptFinalHop ()
724
+
725
+ // Once our interceptor is set up, we can send the blinded payment.
726
+ testCase .sendBlindedPayment (ctx , blindedRoute )
727
+
728
+ // Wait for the HTLC to be active on Alice's channel.
729
+ hash := sha256 .Sum256 (testCase .preimage [:])
730
+ ht .AssertOutgoingHTLCActive (ht .Alice , testCase .channels [0 ], hash [:])
731
+ ht .AssertOutgoingHTLCActive (ht .Bob , testCase .channels [1 ], hash [:])
732
+
733
+ // Intercept and settle the HTLC.
734
+ settleHTLC ()
735
+
736
+ // Assert that the HTLC has settled before test cleanup runs so that
737
+ // we can cooperatively close all channels.
738
+ ht .AssertHLTCNotActive (ht .Bob , testCase .channels [1 ], hash [:])
739
+ ht .AssertHLTCNotActive (ht .Alice , testCase .channels [0 ], hash [:])
623
740
}
0 commit comments