11package  itest
22
33import  (
4+ 	"bytes" 
45	"context" 
56	"fmt" 
67	"math" 
@@ -18,12 +19,14 @@ import (
1819	"github.com/lightninglabs/taproot-assets/tapchannel" 
1920	"github.com/lightninglabs/taproot-assets/taprpc" 
2021	"github.com/lightninglabs/taproot-assets/taprpc/mintrpc" 
22+ 	"github.com/lightninglabs/taproot-assets/taprpc/rfqrpc" 
2123	tchrpc "github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc" 
2224	"github.com/lightninglabs/taproot-assets/taprpc/universerpc" 
2325	"github.com/lightninglabs/taproot-assets/tapscript" 
2426	"github.com/lightningnetwork/lnd/fn" 
2527	"github.com/lightningnetwork/lnd/lnrpc" 
2628	"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" 
29+ 	"github.com/lightningnetwork/lnd/lnrpc/routerrpc" 
2730	"github.com/lightningnetwork/lnd/lnrpc/walletrpc" 
2831	"github.com/lightningnetwork/lnd/lntest" 
2932	"github.com/lightningnetwork/lnd/lntest/port" 
4750
4851	shortTimeout  =  time .Second  *  5 
4952
50- 	defaultPaymentStatus  =  fn .None [lnrpc.Payment_PaymentStatus ]()
53+ 	defaultPaymentStatusOpt  =  fn .None [lnrpc.Payment_PaymentStatus ]()
5154)
5255
5356var  (
@@ -227,7 +230,7 @@ func testCustomChannelsLarge(_ context.Context, net *NetworkHarness,
227230	)
228231	payInvoiceWithAssets (
229232		t .t , charlie , dave , invoiceResp .PaymentRequest , assetID , false ,
230- 		defaultPaymentStatus ,
233+ 		defaultPaymentStatusOpt ,
231234	)
232235	logBalance (t .t , nodes , assetID , "after invoice" )
233236
@@ -242,7 +245,7 @@ func testCustomChannelsLarge(_ context.Context, net *NetworkHarness,
242245
243246	payInvoiceWithAssets (
244247		t .t , fabia , erin , invoiceResp2 .PaymentRequest , assetID , false ,
245- 		defaultPaymentStatus ,
248+ 		defaultPaymentStatusOpt ,
246249	)
247250	logBalance (t .t , nodes , assetID , "after invoice 2" )
248251
@@ -253,7 +256,7 @@ func testCustomChannelsLarge(_ context.Context, net *NetworkHarness,
253256	)
254257	payInvoiceWithAssets (
255258		t .t , charlie , dave , invoiceResp3 .PaymentRequest , assetID , false ,
256- 		defaultPaymentStatus ,
259+ 		defaultPaymentStatusOpt ,
257260	)
258261	logBalance (t .t , nodes , assetID , "after invoice 3" )
259262
@@ -445,7 +448,7 @@ func testCustomChannels(_ context.Context, net *NetworkHarness,
445448	)
446449	payInvoiceWithAssets (
447450		t .t , dave , charlie , invoiceResp .PaymentRequest , assetID , true ,
448- 		defaultPaymentStatus ,
451+ 		defaultPaymentStatusOpt ,
449452	)
450453	logBalance (t .t , nodes , assetID , "after invoice back" )
451454
@@ -510,7 +513,7 @@ func testCustomChannels(_ context.Context, net *NetworkHarness,
510513	)
511514	payInvoiceWithAssets (
512515		t .t , charlie , dave , invoiceResp .PaymentRequest , assetID , true ,
513- 		defaultPaymentStatus ,
516+ 		defaultPaymentStatusOpt ,
514517	)
515518	logBalance (t .t , nodes , assetID , "after invoice" )
516519
@@ -555,7 +558,7 @@ func testCustomChannels(_ context.Context, net *NetworkHarness,
555558	)
556559	payInvoiceWithAssets (
557560		t .t , charlie , dave , invoiceResp .PaymentRequest , assetID , true ,
558- 		defaultPaymentStatus ,
561+ 		defaultPaymentStatusOpt ,
559562	)
560563	logBalance (t .t , nodes , assetID , "after invoice" )
561564
@@ -592,7 +595,7 @@ func testCustomChannels(_ context.Context, net *NetworkHarness,
592595	)
593596	payInvoiceWithAssets (
594597		t .t , charlie , dave , invoiceResp .PaymentRequest , assetID , true ,
595- 		defaultPaymentStatus ,
598+ 		defaultPaymentStatusOpt ,
596599	)
597600	logBalance (t .t , nodes , assetID , "after invoice" )
598601
@@ -613,7 +616,7 @@ func testCustomChannels(_ context.Context, net *NetworkHarness,
613616	)
614617	payInvoiceWithAssets (
615618		t .t , charlie , dave , invoiceResp .PaymentRequest , assetID , true ,
616- 		defaultPaymentStatus ,
619+ 		defaultPaymentStatusOpt ,
617620	)
618621	logBalance (t .t , nodes , assetID , "after asset-to-asset" )
619622
@@ -949,7 +952,7 @@ func testCustomChannelsGroupedAsset(_ context.Context, net *NetworkHarness,
949952	)
950953	payInvoiceWithAssets (
951954		t .t , charlie , dave , invoiceResp .PaymentRequest , assetID , true ,
952- 		defaultPaymentStatus ,
955+ 		defaultPaymentStatusOpt ,
953956	)
954957	logBalance (t .t , nodes , assetID , "after invoice" )
955958
@@ -987,7 +990,7 @@ func testCustomChannelsGroupedAsset(_ context.Context, net *NetworkHarness,
987990	)
988991	payInvoiceWithAssets (
989992		t .t , charlie , dave , invoiceResp .PaymentRequest , assetID , true ,
990- 		defaultPaymentStatus ,
993+ 		defaultPaymentStatusOpt ,
991994	)
992995	logBalance (t .t , nodes , assetID , "after invoice" )
993996
@@ -1024,7 +1027,7 @@ func testCustomChannelsGroupedAsset(_ context.Context, net *NetworkHarness,
10241027	)
10251028	payInvoiceWithAssets (
10261029		t .t , charlie , dave , invoiceResp .PaymentRequest , assetID , true ,
1027- 		defaultPaymentStatus ,
1030+ 		defaultPaymentStatusOpt ,
10281031	)
10291032	logBalance (t .t , nodes , assetID , "after invoice" )
10301033
@@ -1045,7 +1048,7 @@ func testCustomChannelsGroupedAsset(_ context.Context, net *NetworkHarness,
10451048	)
10461049	payInvoiceWithAssets (
10471050		t .t , charlie , dave , invoiceResp .PaymentRequest , assetID , true ,
1048- 		defaultPaymentStatus ,
1051+ 		defaultPaymentStatusOpt ,
10491052	)
10501053	logBalance (t .t , nodes , assetID , "after asset-to-asset" )
10511054
@@ -1805,7 +1808,7 @@ func testCustomChannelsLiquidityEdgeCases(_ context.Context,
18051808			SatPerVByte : 5 ,
18061809		},
18071810	)
1808- 	defer  closeChannelAndAssert (t , net , dave , channelOp , false )
1811+ 	defer  closeChannelAndAssert (t , net , dave , channelOp , true )
18091812
18101813	// This is the only public channel, we need everyone to be aware of it. 
18111814	assertChannelKnown (t .t , charlie , channelOp )
@@ -1958,7 +1961,7 @@ func testCustomChannelsLiquidityEdgeCases(_ context.Context,
19581961
19591962	payInvoiceWithAssets (
19601963		t .t , charlie , dave , invoiceResp .PaymentRequest , assetID , false ,
1961- 		defaultPaymentStatus ,
1964+ 		defaultPaymentStatusOpt ,
19621965	)
19631966
19641967	logBalance (t .t , nodes , assetID , "after big asset payment (asset " + 
@@ -2004,7 +2007,7 @@ func testCustomChannelsLiquidityEdgeCases(_ context.Context,
20042007
20052008	payInvoiceWithAssets (
20062009		t .t , yara , dave , invoiceResp .PaymentRequest , assetID , false ,
2007- 		defaultPaymentStatus ,
2010+ 		defaultPaymentStatusOpt ,
20082011	)
20092012
20102013	logBalance (t .t , nodes , assetID , "after big asset payment (asset " + 
@@ -2020,10 +2023,11 @@ func testCustomChannelsLiquidityEdgeCases(_ context.Context,
20202023	logBalance (t .t , nodes , assetID , "after small payment (asset " + 
20212024		"invoice, <354sats)" )
20222025
2023- 	// Edge case: Now Charlie  creates an asset invoice to be paid for by 
2026+ 	// Edge case: Now Dave  creates an asset invoice to be paid for by 
20242027	// Yara with satoshi. For the last hop we try to settle the invoice in 
2025- 	// satoshi, where we will check whether Charlie's strict forwarding 
2026- 	// works as expected. 
2028+ 	// satoshi, where we will check whether Dave's strict forwarding works 
2029+ 	// as expected. Charlie is only used as a dummy RFQ peer in this case, 
2030+ 	// Yara totally ignored the RFQ hint and pays agnostically with sats. 
20272031	invoiceResp  =  createAssetInvoice (
20282032		t .t , charlie , dave , 1 , assetID ,
20292033	)
@@ -2046,6 +2050,166 @@ func testCustomChannelsLiquidityEdgeCases(_ context.Context,
20462050
20472051	logBalance (t .t , nodes , assetID , "after failed payment (asset " + 
20482052		"invoice, strict forwarding)" )
2053+ 
2054+ 	// Edge case: Fabia gets all the asset liquidity on her side. Then 
2055+ 	// generates an asset invoice to be paid for with assets by Charlie. 
2056+ 	// Charlie will unleash multiple shards towards Fabia, exhausting the 
2057+ 	// liquidity in the Erin<->Fabia channel. Dave should be able to detect 
2058+ 	// those failures and account for those cancelled HTLCs in the RFQ 
2059+ 	// tracking mechanism. Eventually we will slosh some funds from Fabia 
2060+ 	// back to Erin in order for Erin to be able to forward the rest of the 
2061+ 	// HTLCs, leading to the release of the preimage. 
2062+ 
2063+ 	// Erin starts by sending half of the assets to Fabia's side. He also 
2064+ 	// sends some raw sats to be used by Fabia later for the sloshing. 
2065+ 	sendAssetKeySendPayment (
2066+ 		t .t , erin , fabia , 150_000 , assetID ,
2067+ 		fn .None [int64 ](), lnrpc .Payment_SUCCEEDED ,
2068+ 		fn .None [lnrpc.PaymentFailureReason ](),
2069+ 	)
2070+ 	sendKeySendPayment (t .t , erin , fabia , 20_000 )
2071+ 
2072+ 	logBalance (t .t , nodes , assetID , "balance after 1st slosh" )
2073+ 
2074+ 	// Fabia creates an asset invoice of 100k assets. There is currently 
2075+ 	// not enough asset liquidity on Erin's side to forward HTLCs to satisfy 
2076+ 	// this invoice. 
2077+ 	invoiceResp  =  createAssetInvoice (t .t , erin , fabia , 100_000 , assetID )
2078+ 
2079+ 	// We set the waiting period for the slosh payment to occur. This is set 
2080+ 	// to half of the default payment timeout, as we want it to occur half 
2081+ 	// way through the in-flight payment. 
2082+ 	sloshWait  :=  PaymentTimeout  /  3 
2083+ 
2084+ 	go  func () {
2085+ 		// After a small delay (less than the payment timeout) Fabia 
2086+ 		// sloshes back the asset liquidity to Erin. This should allow 
2087+ 		// the payment by Charlie to eventually complete. 
2088+ 		time .Sleep (sloshWait )
2089+ 		sendAssetKeySendPayment (
2090+ 			t .t , fabia , erin , 75_000 , assetID ,
2091+ 			fn .None [int64 ](), lnrpc .Payment_SUCCEEDED ,
2092+ 			fn .None [lnrpc.PaymentFailureReason ](),
2093+ 		)
2094+ 	}()
2095+ 
2096+ 	// To avoid goroutine uncertainty, we wait as much as the above routine 
2097+ 	// minus a small delta. This is enough for us to be sure that the 
2098+ 	// payment will complete due to the slosh. 
2099+ 	timeoutChan  =  time .After (sloshWait  -  time .Millisecond * 250 )
2100+ 	done  =  make (chan  bool , 1 )
2101+ 
2102+ 	go  func () {
2103+ 		// Now Charlie pays the invoice with assets. What happens on the 
2104+ 		// Charlie-Dave channel doesn't matter. This payment will block 
2105+ 		// until the previous slosh payment completes. That slosh will 
2106+ 		// allow for the rest of the HTLCs to be forwarded. 
2107+ 		payInvoiceWithAssets (
2108+ 			t .t , charlie , dave , invoiceResp .PaymentRequest , assetID ,
2109+ 			true , defaultPaymentStatusOpt ,
2110+ 		)
2111+ 
2112+ 		done  <-  true 
2113+ 	}()
2114+ 
2115+ 	select  {
2116+ 	case  <- done :
2117+ 		// If the payment completes before the slosh payment occurs then 
2118+ 		// something went wrong, this is not the expected test case 
2119+ 		// behavior. 
2120+ 		t .Fatalf ("payment completed before expected block period" )
2121+ 	case  <- timeoutChan :
2122+ 		// The expected time delay for the payment to complete has been 
2123+ 		// passed, so now we wait for the payment to complete. 
2124+ 		<- done 
2125+ 	}
2126+ 
2127+ 	logBalance (t .t , nodes , assetID , "after htlc track cancel" )
2128+ 
2129+ 	// Edge case: Charlie negotiates a quote with Dave which has a low max 
2130+ 	// amount (~170k sats). Then Charlie creates an invoice with a total 
2131+ 	// amount slightly larger than the max allowed in the quote (200k sats). 
2132+ 	// Erin will try to pay that invoice with sats, in shards of max size 
2133+ 	// 80k sats. Dave will eventually stop forwarding HTLCs as the RFQ HTLC 
2134+ 	// tracking mechanism should stop them from being forwarded, as they 
2135+ 	// violate the maximum allowed amount of the quote. 
2136+ 
2137+ 	// Charlie starts by negotiating the quote. 
2138+ 	res , err  :=  charlieTap .RfqClient .AddAssetBuyOrder (
2139+ 		ctxb , & rfqrpc.AddAssetBuyOrderRequest {
2140+ 			AssetSpecifier : & rfqrpc.AssetSpecifier {
2141+ 				Id : & rfqrpc.AssetSpecifier_AssetId {
2142+ 					AssetId : assetID ,
2143+ 				},
2144+ 			},
2145+ 			AssetMaxAmt :    10_000 ,
2146+ 			Expiry :         uint64 (time .Now ().Add (time .Hour ).Unix ()),
2147+ 			PeerPubKey :     dave .PubKey [:],
2148+ 			TimeoutSeconds : 10 ,
2149+ 		},
2150+ 	)
2151+ 	require .NoError (t .t , err )
2152+ 
2153+ 	quote , ok  :=  res .Response .(* rfqrpc.AddAssetBuyOrderResponse_AcceptedQuote )
2154+ 	require .True (t .t , ok )
2155+ 
2156+ 	// We now manually add the invoice in order to inject the above, 
2157+ 	// manually generated, quote. 
2158+ 	iResp , err  :=  charlie .AddInvoice (ctxb , & lnrpc.Invoice {
2159+ 		Memo :       "" ,
2160+ 		Value :      200_000 ,
2161+ 		RPreimage :  bytes .Repeat ([]byte {11 }, 32 ),
2162+ 		CltvExpiry : 60 ,
2163+ 		RouteHints : []* lnrpc.RouteHint {
2164+ 			& lnrpc.RouteHint {
2165+ 				HopHints : []* lnrpc.HopHint {
2166+ 					& lnrpc.HopHint {
2167+ 						NodeId : dave .PubKeyStr ,
2168+ 						ChanId : quote .AcceptedQuote .Scid ,
2169+ 					},
2170+ 				},
2171+ 			},
2172+ 		},
2173+ 	})
2174+ 	require .NoError (t .t , err )
2175+ 
2176+ 	// Now Erin tries to pay the invoice. Since the multipart payment will 
2177+ 	// have some of its shards failing the pathfinding logic will keep going 
2178+ 	// and we won't see a payment failure but a timeout. If a final outcome 
2179+ 	// is not produced within a reasonable amount of time, we assume the 
2180+ 	// payment is still trying to find a route, therefore the HTLC rejection 
2181+ 	// works. 
2182+ 	timeoutChan  =  time .After (PaymentTimeout  /  2 )
2183+ 	done  =  make (chan  bool , 1 )
2184+ 
2185+ 	ctxc , cancel  :=  context .WithCancel (context .Background ())
2186+ 
2187+ 	//nolint:lll 
2188+ 	go  func () {
2189+ 		// payInvoiceWithSatoshi(t.t, erin, iResp, lnrpc.Payment_FAILED) 
2190+ 		sendReq  :=  & routerrpc.SendPaymentRequest {
2191+ 			PaymentRequest :   iResp .PaymentRequest ,
2192+ 			TimeoutSeconds :   int32 (PaymentTimeout .Seconds ()),
2193+ 			MaxShardSizeMsat : 80_000_000 ,
2194+ 			FeeLimitMsat :     1_000_000 ,
2195+ 		}
2196+ 		stream , err  :=  erin .RouterClient .SendPaymentV2 (ctxc , sendReq )
2197+ 		if  err  ==  nil  {
2198+ 			_ , _  =  getPaymentResult (stream )
2199+ 		}
2200+ 
2201+ 		done  <-  true 
2202+ 	}()
2203+ 
2204+ 	select  {
2205+ 	case  <- done :
2206+ 		t .Fatalf ("Payment should not produce a final outcome" )
2207+ 
2208+ 	case  <- timeoutChan :
2209+ 		cancel ()
2210+ 	}
2211+ 
2212+ 	logBalance (t .t , nodes , assetID , "after small manual rfq" )
20492213}
20502214
20512215// testCustomChannelsBalanceConsistency is a test that test the balance of nodes 
@@ -2563,7 +2727,7 @@ func testCustomChannelsOraclePricing(_ context.Context,
25632727
25642728	numUnits , rate  :=  payInvoiceWithAssets (
25652729		t .t , charlie , dave , invoiceResp .PaymentRequest , assetID , false ,
2566- 		defaultPaymentStatus ,
2730+ 		defaultPaymentStatusOpt ,
25672731	)
25682732	logBalance (t .t , nodes , assetID , "after invoice" )
25692733
0 commit comments