@@ -355,14 +355,16 @@ func (b *blindedForwardTest) setup(
355355 ctx context.Context ) * routing.BlindedPayment {
356356
357357 b .carol = b .ht .NewNode ("Carol" , []string {
358- "requireinterceptor" ,
358+ "-- requireinterceptor" , "--bitcoin.timelockdelta=18 " ,
359359 })
360360
361361 var err error
362362 b .carolInterceptor , err = b .carol .RPC .Router .HtlcInterceptor (ctx )
363363 require .NoError (b .ht , err , "interceptor" )
364364
365- b .dave = b .ht .NewNode ("Dave" , nil )
365+ b .dave = b .ht .NewNode ("Dave" , []string {
366+ "--bitcoin.timelockdelta=18" ,
367+ })
366368
367369 b .channels = setupFourHopNetwork (b .ht , b .carol , b .dave )
368370
@@ -951,3 +953,106 @@ func testDisableIntroductionNode(ht *lntest.HarnessTest) {
951953 // if Bob _doesn't_ fail the HTLC back as expected.
952954 sendAndResumeBlindedPayment (ctx , ht , testCase , route , false )
953955}
956+
957+ // testErrorHandlingOnChainFailure tests handling of blinded errors when we're
958+ // resolving from an on-chain resolution. This test also tests that we're able
959+ // to resolve blinded HTLCs on chain between restarts, as we've got all the
960+ // infrastructure in place already for error testing.
961+ func testErrorHandlingOnChainFailure (ht * lntest.HarnessTest ) {
962+ // Setup a test case, note that we don't use its built in clean up
963+ // because we're going to close a channel so we'll close out the
964+ // rest manually.
965+ ctx , testCase := newBlindedForwardTest (ht )
966+
967+ // Note that we send a larger amount here do it'll be worthwhile for
968+ // the sweeper to claim.
969+ route := testCase .setup (ctx )
970+ blindedRoute := testCase .createRouteToBlinded (50_000_000 , route )
971+
972+ // Once our interceptor is set up, we can send the blinded payment.
973+ cancelPmt := testCase .sendBlindedPayment (ctx , blindedRoute )
974+ defer cancelPmt ()
975+
976+ // Wait for the HTLC to be active on Alice and Bob's channels.
977+ hash := sha256 .Sum256 (testCase .preimage [:])
978+ ht .AssertOutgoingHTLCActive (ht .Alice , testCase .channels [0 ], hash [:])
979+ ht .AssertOutgoingHTLCActive (ht .Bob , testCase .channels [1 ], hash [:])
980+
981+ // Intercept the forward on Carol's link, but do not take any action
982+ // so that we have the chance to force close with this HTLC in flight.
983+ carolHTLC , err := testCase .carolInterceptor .Recv ()
984+ require .NoError (ht , err )
985+
986+ // Force close Bob <-> Carol.
987+ closeStream , _ := ht .CloseChannelAssertPending (
988+ ht .Bob , testCase .channels [1 ], true ,
989+ )
990+
991+ ht .AssertStreamChannelForceClosed (
992+ ht .Bob , testCase .channels [1 ], false , closeStream ,
993+ )
994+
995+ // SuspendCarol so that she can't interfere with the resolution of the
996+ // HTLC from now on.
997+ restartCarol := ht .SuspendNode (testCase .carol )
998+
999+ // Mine blocks so that Bob will claim his CSV delayed local commitment,
1000+ // we've already mined 1 block so we need one less than our CSV.
1001+ ht .MineBlocks (node .DefaultCSV - 1 )
1002+ ht .AssertNumPendingSweeps (ht .Bob , 1 )
1003+ ht .MineEmptyBlocks (1 )
1004+ ht .Miner .MineBlocksAndAssertNumTxes (1 , 1 )
1005+
1006+ // Restart bob so that we can test that he's able to recover everything
1007+ // he needs to claim a blinded HTLC.
1008+ ht .RestartNode (ht .Bob )
1009+
1010+ // Mine enough blocks for Bob to trigger timeout of his outgoing HTLC.
1011+ // Carol's incoming expiry height is Bob's outgoing so we can use this
1012+ // value.
1013+ info := ht .Bob .RPC .GetInfo ()
1014+ target := carolHTLC .IncomingExpiry - info .BlockHeight
1015+ ht .MineBlocks (target )
1016+
1017+ // Wait for Bob's timeout transaction in the mempool, since we've
1018+ // suspended Carol we don't need to account for her commitment output
1019+ // claim.
1020+ ht .Miner .MineBlocksAndAssertNumTxes (1 , 1 )
1021+
1022+ // Assert that the HTLC has cleared.
1023+ ht .AssertHTLCNotActive (ht .Alice , testCase .channels [0 ], hash [:])
1024+ ht .AssertHTLCNotActive (ht .Bob , testCase .channels [0 ], hash [:])
1025+
1026+ // Wait for the HTLC to reflect as failed for Alice.
1027+ paymentStream := ht .Alice .RPC .TrackPaymentV2 (hash [:])
1028+ htlcs := ht .ReceiveTrackPayment (paymentStream ).Htlcs
1029+ require .Len (ht , htlcs , 1 )
1030+ require .NotNil (ht , htlcs [0 ].Failure )
1031+ require .Equal (
1032+ ht , htlcs [0 ].Failure .Code ,
1033+ lnrpc .Failure_INVALID_ONION_BLINDING ,
1034+ )
1035+
1036+ // Clean up the rest of our force close: mine blocks so that Bob's CSV
1037+ // expires plus one block to trigger his sweep and then mine it.
1038+ ht .MineBlocks (node .DefaultCSV + 1 )
1039+ ht .Miner .MineBlocksAndAssertNumTxes (1 , 1 )
1040+
1041+ // Bring carol back up so that we can close out the rest of our
1042+ // channels cooperatively. She requires an interceptor to start up
1043+ // so we just re-register our interceptor.
1044+ require .NoError (ht , restartCarol ())
1045+ _ , err = testCase .carol .RPC .Router .HtlcInterceptor (ctx )
1046+ require .NoError (ht , err , "interceptor" )
1047+
1048+ // Assert that Carol has started up and reconnected to dave so that
1049+ // we can close out channels cooperatively.
1050+ ht .EnsureConnected (testCase .carol , testCase .dave )
1051+
1052+ // Manually close out the rest of our channels and cancel (don't use
1053+ // built in cleanup which will try close the already-force-closed
1054+ // channel).
1055+ ht .CloseChannel (ht .Alice , testCase .channels [0 ])
1056+ ht .CloseChannel (testCase .carol , testCase .channels [2 ])
1057+ testCase .cancel ()
1058+ }
0 commit comments