@@ -479,33 +479,131 @@ func testForwardInterceptorModifiedHtlc(ht *lntest.HarnessTest) {
479479 ht .CloseChannel (bob , cpBC )
480480}
481481
482+ // testForwardInterceptorWireRecords tests that the interceptor can read any
483+ // wire custom records provided by the sender of a payment as part of the
484+ // update_add_htlc message.
485+ func testForwardInterceptorWireRecords (ht * lntest.HarnessTest ) {
486+ // Initialize the test context with 3 connected nodes.
487+ ts := newInterceptorTestScenario (ht )
488+
489+ alice , bob , carol , dave := ts .alice , ts .bob , ts .carol , ts .dave
490+
491+ // Open and wait for channels.
492+ const chanAmt = btcutil .Amount (300000 )
493+ p := lntest.OpenChannelParams {Amt : chanAmt }
494+ reqs := []* lntest.OpenChannelRequest {
495+ {Local : alice , Remote : bob , Param : p },
496+ {Local : bob , Remote : carol , Param : p },
497+ {Local : carol , Remote : dave , Param : p },
498+ }
499+ resp := ht .OpenMultiChannelsAsync (reqs )
500+ cpAB , cpBC , cpCD := resp [0 ], resp [1 ], resp [2 ]
501+
502+ // Make sure Alice is aware of channel Bob=>Carol.
503+ ht .AssertTopologyChannelOpen (alice , cpBC )
504+
505+ // Connect an interceptor to Bob's node.
506+ bobInterceptor , cancelBobInterceptor := bob .RPC .HtlcInterceptor ()
507+ defer cancelBobInterceptor ()
508+
509+ // Also connect an interceptor on Carol's node to check whether we're
510+ // relaying the TLVs send in update_add_htlc over Alice -> Bob on the
511+ // Bob -> Carol link.
512+ carolInterceptor , cancelCarolInterceptor := carol .RPC .HtlcInterceptor ()
513+ defer cancelCarolInterceptor ()
514+
515+ req := & lnrpc.Invoice {ValueMsat : 1000 }
516+ addResponse := dave .RPC .AddInvoice (req )
517+ invoice := dave .RPC .LookupInvoice (addResponse .RHash )
518+
519+ sendReq := & routerrpc.SendPaymentRequest {
520+ PaymentRequest : invoice .PaymentRequest ,
521+ TimeoutSeconds : int32 (wait .PaymentTimeout .Seconds ()),
522+ FeeLimitMsat : noFeeLimitMsat ,
523+ FirstHopCustomRecords : map [uint64 ][]byte {
524+ 65537 : []byte ("test" ),
525+ },
526+ }
527+
528+ _ = alice .RPC .SendPayment (sendReq )
529+
530+ // We start the htlc interceptor with a simple implementation that saves
531+ // all intercepted packets. These packets are held to simulate a
532+ // pending payment.
533+ packet := ht .ReceiveHtlcInterceptor (bobInterceptor )
534+
535+ require .Len (ht , packet .IncomingHtlcWireCustomRecords , 1 )
536+
537+ val , ok := packet .IncomingHtlcWireCustomRecords [65537 ]
538+ require .True (ht , ok , "expected custom record" )
539+ require .Equal (ht , []byte ("test" ), val )
540+
541+ action := routerrpc .ResolveHoldForwardAction_RESUME_MODIFIED
542+ newOutgoingAmountMsat := packet .OutgoingAmountMsat + 800
543+
544+ err := bobInterceptor .Send (& routerrpc.ForwardHtlcInterceptResponse {
545+ IncomingCircuitKey : packet .IncomingCircuitKey ,
546+ OutgoingAmountMsat : newOutgoingAmountMsat ,
547+ Action : action ,
548+ })
549+ require .NoError (ht , err , "failed to send request" )
550+
551+ // Assert that the Alice -> Bob custom records in update_add_htlc are
552+ // not propagated on the Bob -> Carol link.
553+ packet = ht .ReceiveHtlcInterceptor (carolInterceptor )
554+ require .Len (ht , packet .IncomingHtlcWireCustomRecords , 0 )
555+
556+ // Just resume the payment on Carol.
557+ err = carolInterceptor .Send (& routerrpc.ForwardHtlcInterceptResponse {
558+ IncomingCircuitKey : packet .IncomingCircuitKey ,
559+ Action : routerrpc .ResolveHoldForwardAction_RESUME ,
560+ })
561+ require .NoError (ht , err , "carol interceptor response" )
562+
563+ // Assert that the payment was successful.
564+ var preimage lntypes.Preimage
565+ copy (preimage [:], invoice .RPreimage )
566+ ht .AssertPaymentStatus (alice , preimage , lnrpc .Payment_SUCCEEDED )
567+
568+ // Finally, close channels.
569+ ht .CloseChannel (alice , cpAB )
570+ ht .CloseChannel (bob , cpBC )
571+ ht .CloseChannel (carol , cpCD )
572+ }
573+
482574// interceptorTestScenario is a helper struct to hold the test context and
483575// provide the needed functionality.
484576type interceptorTestScenario struct {
485- ht * lntest.HarnessTest
486- alice , bob , carol * node.HarnessNode
577+ ht * lntest.HarnessTest
578+ alice , bob , carol , dave * node.HarnessNode
487579}
488580
489581// newInterceptorTestScenario initializes a new test scenario with three nodes
490582// and connects them to have the following topology,
491583//
492- // Alice --> Bob --> Carol
584+ // Alice --> Bob --> Carol --> Dave
493585//
494586// Among them, Alice and Bob are standby nodes and Carol is a new node.
495587func newInterceptorTestScenario (
496588 ht * lntest.HarnessTest ) * interceptorTestScenario {
497589
498590 alice , bob := ht .Alice , ht .Bob
499591 carol := ht .NewNode ("carol" , nil )
592+ dave := ht .NewNode ("dave" , nil )
500593
501594 ht .EnsureConnected (alice , bob )
502595 ht .EnsureConnected (bob , carol )
596+ ht .EnsureConnected (carol , dave )
597+
598+ // So that carol can open channels.
599+ ht .FundCoins (btcutil .SatoshiPerBitcoin , carol )
503600
504601 return & interceptorTestScenario {
505602 ht : ht ,
506603 alice : alice ,
507604 bob : bob ,
508605 carol : carol ,
606+ dave : dave ,
509607 }
510608}
511609
0 commit comments