@@ -20,6 +20,7 @@ import (
2020	"github.com/lightninglabs/taproot-assets/tapchannel" 
2121	"github.com/lightninglabs/taproot-assets/taprpc" 
2222	"github.com/lightninglabs/taproot-assets/taprpc/mintrpc" 
23+ 	oraclerpc "github.com/lightninglabs/taproot-assets/taprpc/priceoraclerpc" 
2324	"github.com/lightninglabs/taproot-assets/taprpc/rfqrpc" 
2425	tchrpc "github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc" 
2526	"github.com/lightninglabs/taproot-assets/taprpc/universerpc" 
@@ -1730,7 +1731,7 @@ func testCustomChannelsBreach(_ context.Context, net *NetworkHarness,
17301731
17311732// testCustomChannelsLiquidityEdgeCases is a test that runs through some 
17321733// taproot asset channel liquidity related edge cases. 
1733- func  testCustomChannelsLiquidityEdgeCases (_  context.Context ,
1734+ func  testCustomChannelsLiquidityEdgeCases (ctxb  context.Context ,
17341735	net  * NetworkHarness , t  * harnessTest ) {
17351736
17361737	lndArgs  :=  slices .Clone (lndArgsTemplate )
@@ -2009,11 +2010,8 @@ func testCustomChannelsLiquidityEdgeCases(_ context.Context,
20092010	// satoshi, where we will check whether Dave's strict forwarding works 
20102011	// as expected. Charlie is only used as a dummy RFQ peer in this case, 
20112012	// Yara totally ignored the RFQ hint and pays agnostically with sats. 
2012- 	invoiceResp  =  createAssetInvoice (
2013- 		t .t , charlie , dave , 1 , assetID ,
2014- 	)
2013+ 	invoiceResp  =  createAssetInvoice (t .t , charlie , dave , 1 , assetID )
20152014
2016- 	ctxb  :=  context .Background ()
20172015	stream , err  :=  dave .InvoicesClient .SubscribeSingleInvoice (
20182016		ctxb , & invoicesrpc.SubscribeSingleInvoiceRequest {
20192017			RHash : invoiceResp .RHash ,
@@ -3434,3 +3432,223 @@ func runCustomChannelsHtlcForceClose(ctxb context.Context, t *harnessTest,
34343432		t .t , zaneTap , assetID , zaneExpectedBalance ,
34353433	)
34363434}
3435+ 
3436+ // testCustomChannelsForwardBandwidth is a test that runs through some Taproot 
3437+ // Assets Channel liquidity edge cases, specifically related to forwarding HTLCs 
3438+ // into channels with no available asset bandwidth. 
3439+ func  testCustomChannelsForwardBandwidth (ctxb  context.Context ,
3440+ 	net  * NetworkHarness , t  * harnessTest ) {
3441+ 
3442+ 	lndArgs  :=  slices .Clone (lndArgsTemplate )
3443+ 	litdArgs  :=  slices .Clone (litdArgsTemplate )
3444+ 
3445+ 	// Explicitly set the proof courier as Zane (now has no other role 
3446+ 	// other than proof shuffling), otherwise a hashmail courier will be 
3447+ 	// used. For the funding transaction, we're just posting it and don't 
3448+ 	// expect a true receiver. 
3449+ 	zane , err  :=  net .NewNode (
3450+ 		t .t , "Zane" , lndArgs , false , true , litdArgs ... ,
3451+ 	)
3452+ 	require .NoError (t .t , err )
3453+ 
3454+ 	litdArgs  =  append (litdArgs , fmt .Sprintf (
3455+ 		"--taproot-assets.proofcourieraddr=%s://%s" ,
3456+ 		proof .UniverseRpcCourierType , zane .Cfg .LitAddr (),
3457+ 	))
3458+ 
3459+ 	// The topology we are going for looks like the following: 
3460+ 	// 
3461+ 	// Charlie  --[assets]-->  Dave  --[sats]-->  Erin  --[assets]-->  Fabia 
3462+ 	//                          | 
3463+ 	//                          | 
3464+ 	//                       [assets] 
3465+ 	//                          | 
3466+ 	//                          v 
3467+ 	//                        Yara 
3468+ 	// 
3469+ 	// With [assets] being a custom channel and [sats] being a normal, BTC 
3470+ 	// only channel. 
3471+ 	// All 5 nodes need to be full litd nodes running in integrated mode 
3472+ 	// with tapd included. We also need specific flags to be enabled, so we 
3473+ 	// create 5 completely new nodes, ignoring the two default nodes that 
3474+ 	// are created by the harness. 
3475+ 	charlie , err  :=  net .NewNode (
3476+ 		t .t , "Charlie" , lndArgs , false , true , litdArgs ... ,
3477+ 	)
3478+ 	require .NoError (t .t , err )
3479+ 
3480+ 	dave , err  :=  net .NewNode (t .t , "Dave" , lndArgs , false , true , litdArgs ... )
3481+ 	require .NoError (t .t , err )
3482+ 	erin , err  :=  net .NewNode (t .t , "Erin" , lndArgs , false , true , litdArgs ... )
3483+ 	require .NoError (t .t , err )
3484+ 	fabia , err  :=  net .NewNode (
3485+ 		t .t , "Fabia" , lndArgs , false , true , litdArgs ... ,
3486+ 	)
3487+ 	require .NoError (t .t , err )
3488+ 	yara , err  :=  net .NewNode (
3489+ 		t .t , "Yara" , lndArgs , false , true , litdArgs ... ,
3490+ 	)
3491+ 	require .NoError (t .t , err )
3492+ 
3493+ 	nodes  :=  []* HarnessNode {charlie , dave , erin , fabia , yara }
3494+ 	connectAllNodes (t .t , net , nodes )
3495+ 	fundAllNodes (t .t , net , nodes )
3496+ 
3497+ 	// Create the normal channel between Dave and Erin. 
3498+ 	t .Logf ("Opening normal channel between Dave and Erin..." )
3499+ 	channelOp  :=  openChannelAndAssert (
3500+ 		t , net , dave , erin , lntest.OpenChannelParams {
3501+ 			Amt :         10_000_000 ,
3502+ 			SatPerVByte : 5 ,
3503+ 		},
3504+ 	)
3505+ 	defer  closeChannelAndAssert (t , net , dave , channelOp , false )
3506+ 
3507+ 	// This is the only public channel, we need everyone to be aware of it. 
3508+ 	assertChannelKnown (t .t , charlie , channelOp )
3509+ 	assertChannelKnown (t .t , fabia , channelOp )
3510+ 
3511+ 	universeTap  :=  newTapClient (t .t , zane )
3512+ 	charlieTap  :=  newTapClient (t .t , charlie )
3513+ 	daveTap  :=  newTapClient (t .t , dave )
3514+ 	erinTap  :=  newTapClient (t .t , erin )
3515+ 	fabiaTap  :=  newTapClient (t .t , fabia )
3516+ 	yaraTap  :=  newTapClient (t .t , yara )
3517+ 
3518+ 	// Mint an asset on Charlie and sync all nodes to Charlie as the 
3519+ 	// universe. 
3520+ 	mintedAssets  :=  itest .MintAssetsConfirmBatch (
3521+ 		t .t , t .lndHarness .Miner .Client , charlieTap ,
3522+ 		[]* mintrpc.MintAssetRequest {
3523+ 			{
3524+ 				Asset : itestAsset ,
3525+ 			},
3526+ 		},
3527+ 	)
3528+ 	cents  :=  mintedAssets [0 ]
3529+ 	assetID  :=  cents .AssetGenesis .AssetId 
3530+ 
3531+ 	t .Logf ("Minted %d lightning cents, syncing universes..." , cents .Amount )
3532+ 	syncUniverses (t .t , charlieTap , dave , erin , fabia , yara )
3533+ 	t .Logf ("Universes synced between all nodes, distributing assets..." )
3534+ 
3535+ 	const  (
3536+ 		daveFundingAmount  =  uint64 (400_000 )
3537+ 		erinFundingAmount  =  uint64 (200_000 )
3538+ 	)
3539+ 	charlieFundingAmount  :=  cents .Amount  -  uint64 (2 * 400_000 )
3540+ 
3541+ 	_ , _ , chanPointEF  :=  createTestAssetNetwork (
3542+ 		t , net , charlieTap , daveTap , erinTap , fabiaTap , yaraTap ,
3543+ 		universeTap , cents , 400_000 , charlieFundingAmount ,
3544+ 		daveFundingAmount , erinFundingAmount , 0 ,
3545+ 	)
3546+ 
3547+ 	// Before we start sending out payments, let's make sure each node can 
3548+ 	// see the other one in the graph and has all required features. 
3549+ 	require .NoError (t .t , t .lndHarness .AssertNodeKnown (charlie , dave ))
3550+ 	require .NoError (t .t , t .lndHarness .AssertNodeKnown (dave , charlie ))
3551+ 	require .NoError (t .t , t .lndHarness .AssertNodeKnown (dave , yara ))
3552+ 	require .NoError (t .t , t .lndHarness .AssertNodeKnown (yara , dave ))
3553+ 	require .NoError (t .t , t .lndHarness .AssertNodeKnown (erin , fabia ))
3554+ 	require .NoError (t .t , t .lndHarness .AssertNodeKnown (fabia , erin ))
3555+ 	require .NoError (t .t , t .lndHarness .AssertNodeKnown (charlie , erin ))
3556+ 
3557+ 	logBalance (t .t , nodes , assetID , "initial" )
3558+ 
3559+ 	// We now deplete the channel between Erin and Fabia by moving all 
3560+ 	// assets to Fabia. 
3561+ 	sendAssetKeySendPayment (
3562+ 		t .t , erin , fabia , erinFundingAmount , assetID , fn .None [int64 ](),
3563+ 	)
3564+ 	logBalance (t .t , nodes , assetID , "after moving assets to Fabia" )
3565+ 
3566+ 	// Test case 1: We cannot keysend more assets from Erin to Fabia. 
3567+ 	sendAssetKeySendPayment (
3568+ 		t .t , erin , fabia , 1 , assetID , fn .None [int64 ](),
3569+ 		withFailure (lnrpc .Payment_FAILED , failureNoBalance ),
3570+ 	)
3571+ 
3572+ 	// Test case 2: We cannot pay an invoice from Charlie to Fabia. 
3573+ 	invoiceResp  :=  createAssetInvoice (t .t , erin , fabia , 123 , assetID )
3574+ 	payInvoiceWithSatoshi (
3575+ 		t .t , charlie , invoiceResp ,
3576+ 		withFailure (lnrpc .Payment_FAILED , failureNoRoute ),
3577+ 	)
3578+ 
3579+ 	// Test case 3: We now create an asset buy order for a normal amount of 
3580+ 	// assets. We then "fake" an invoice referencing that buy order that 
3581+ 	// is for an amount that is too small to be paid with a single asset 
3582+ 	// unit. This should be handled gracefully and not lead to a crash. 
3583+ 	// Ideally such an invoice shouldn't be created in the first place, but 
3584+ 	// we want to make sure that the system doesn't crash in this case. 
3585+ 	numUnits  :=  uint64 (10 )
3586+ 	buyOrderResp , err  :=  fabiaTap .RfqClient .AddAssetBuyOrder (
3587+ 		ctxb , & rfqrpc.AddAssetBuyOrderRequest {
3588+ 			AssetSpecifier : & rfqrpc.AssetSpecifier {
3589+ 				Id : & rfqrpc.AssetSpecifier_AssetId {
3590+ 					AssetId : assetID ,
3591+ 				},
3592+ 			},
3593+ 			AssetMaxAmt : numUnits ,
3594+ 			Expiry : uint64 (
3595+ 				time .Now ().Add (time .Hour ).Unix (),
3596+ 			),
3597+ 			PeerPubKey :     erin .PubKey [:],
3598+ 			TimeoutSeconds : 10 ,
3599+ 		},
3600+ 	)
3601+ 	require .NoError (t .t , err )
3602+ 
3603+ 	quoteResp  :=  buyOrderResp .Response 
3604+ 	quote , ok  :=  quoteResp .(* rfqrpc.AddAssetBuyOrderResponse_AcceptedQuote )
3605+ 	require .True (t .t , ok )
3606+ 
3607+ 	// We calculate the milli-satoshi amount one below the equivalent of a 
3608+ 	// single asset unit. 
3609+ 	rate , err  :=  oraclerpc .UnmarshalFixedPoint (& oraclerpc.FixedPoint {
3610+ 		Coefficient : quote .AcceptedQuote .AskAssetRate .Coefficient ,
3611+ 		Scale :       quote .AcceptedQuote .AskAssetRate .Scale ,
3612+ 	})
3613+ 	require .NoError (t .t , err )
3614+ 
3615+ 	oneUnit  :=  uint64 (1 )
3616+ 	oneUnitFP  :=  rfqmath .NewBigIntFixedPoint (oneUnit , 0 )
3617+ 	oneUnitMilliSat  :=  rfqmath .UnitsToMilliSatoshi (oneUnitFP , * rate )
3618+ 
3619+ 	t .Logf ("Got quote for %v asset units per BTC" , rate )
3620+ 	msatPerUnit  :=  float64 (oneUnitMilliSat ) /  float64 (oneUnit )
3621+ 	t .Logf ("Got quote for %v asset units at %3f msat/unit from peer %s " + 
3622+ 		"with SCID %d" , numUnits , msatPerUnit , erin .PubKeyStr ,
3623+ 		quote .AcceptedQuote .Scid )
3624+ 
3625+ 	// We now manually add the invoice in order to inject the above, 
3626+ 	// manually generated, quote. 
3627+ 	invoiceResp2 , err  :=  fabia .AddInvoice (ctxb , & lnrpc.Invoice {
3628+ 		Memo :      "too small invoice" ,
3629+ 		ValueMsat : int64 (oneUnitMilliSat  -  1 ),
3630+ 		RouteHints : []* lnrpc.RouteHint {{
3631+ 			HopHints : []* lnrpc.HopHint {{
3632+ 				NodeId : erin .PubKeyStr ,
3633+ 				ChanId : quote .AcceptedQuote .Scid ,
3634+ 			}},
3635+ 		}},
3636+ 	})
3637+ 	require .NoError (t .t , err )
3638+ 
3639+ 	payInvoiceWithSatoshi (t .t , dave , invoiceResp2 , withFailure (
3640+ 		lnrpc .Payment_FAILED , failureNoRoute ,
3641+ 	))
3642+ 
3643+ 	// Let's make sure we can still use the channel between Erin and Fabia 
3644+ 	// by doing a satoshi keysend payment. 
3645+ 	sendKeySendPayment (t .t , erin , fabia , 2000 )
3646+ 	logBalance (t .t , nodes , assetID , "after BTC only keysend" )
3647+ 
3648+ 	// Finally, we close the channel between Erin and Fabia to make sure 
3649+ 	// everything is settled correctly. 
3650+ 	closeAssetChannelAndAssert (
3651+ 		t , net , erin , fabia , chanPointEF , assetID , nil ,
3652+ 		universeTap , noOpCoOpCloseBalanceCheck ,
3653+ 	)
3654+ }
0 commit comments