@@ -2892,6 +2892,144 @@ func testCustomChannelsLiquidityEdgeCasesGroup(ctx context.Context,
28922892 testCustomChannelsLiquidityEdgeCasesCore (ctx , net , t , true )
28932893}
28942894
2895+ // testCustomChannelsMultiRFQReceive tests that a node creating an invoice with
2896+ // multiple RFQ quotes can actually guide the payer into using multiple private
2897+ // taproot asset channels to pay the invoice.
2898+ func testCustomChannelsMultiRFQReceive (ctx context.Context , net * NetworkHarness ,
2899+ t * harnessTest ) {
2900+
2901+ lndArgs := slices .Clone (lndArgsTemplate )
2902+ litdArgs := slices .Clone (litdArgsTemplate )
2903+
2904+ charlie , err := net .NewNode (
2905+ t .t , "Charlie" , lndArgs , false , true , litdArgs ... ,
2906+ )
2907+ require .NoError (t .t , err )
2908+
2909+ litdArgs = append (litdArgs , fmt .Sprintf (
2910+ "--taproot-assets.proofcourieraddr=%s://%s" ,
2911+ proof .UniverseRpcCourierType , charlie .Cfg .LitAddr (),
2912+ ))
2913+
2914+ dave , err := net .NewNode (t .t , "Dave" , lndArgs , false , true , litdArgs ... )
2915+ require .NoError (t .t , err )
2916+ erin , err := net .NewNode (t .t , "Erin" , lndArgs , false , true , litdArgs ... )
2917+ require .NoError (t .t , err )
2918+ fabia , err := net .NewNode (
2919+ t .t , "Fabia" , lndArgs , false , true , litdArgs ... ,
2920+ )
2921+ require .NoError (t .t , err )
2922+ yara , err := net .NewNode (
2923+ t .t , "Yara" , lndArgs , false , true , litdArgs ... ,
2924+ )
2925+ require .NoError (t .t , err )
2926+
2927+ nodes := []* HarnessNode {charlie , dave , erin , fabia , yara }
2928+ connectAllNodes (t .t , net , nodes )
2929+ fundAllNodes (t .t , net , nodes )
2930+
2931+ // Let's create the tap clients.
2932+ charlieTap := newTapClient (t .t , charlie )
2933+ daveTap := newTapClient (t .t , dave )
2934+ erinTap := newTapClient (t .t , erin )
2935+ fabiaTap := newTapClient (t .t , fabia )
2936+ yaraTap := newTapClient (t .t , yara )
2937+
2938+ assetReq := itest .CopyRequest (& mintrpc.MintAssetRequest {
2939+ Asset : itestAsset ,
2940+ })
2941+
2942+ assetReq .Asset .NewGroupedAsset = true
2943+
2944+ // Mint an asset on Charlie and sync all nodes to Charlie as the
2945+ // universe.
2946+ mintedAssets := itest .MintAssetsConfirmBatch (
2947+ t .t , t .lndHarness .Miner .Client , charlieTap ,
2948+ []* mintrpc.MintAssetRequest {assetReq },
2949+ )
2950+ cents := mintedAssets [0 ]
2951+ assetID := cents .AssetGenesis .AssetId
2952+ groupID := cents .GetAssetGroup ().GetTweakedGroupKey ()
2953+
2954+ syncUniverses (t .t , charlieTap , dave , erin , fabia , yara )
2955+
2956+ multiRfqNodes := multiRfqNodes {
2957+ charlie : itestNode {
2958+ Lnd : charlie ,
2959+ Tapd : charlieTap ,
2960+ },
2961+ dave : itestNode {
2962+ Lnd : dave ,
2963+ Tapd : daveTap ,
2964+ },
2965+ erin : itestNode {
2966+ Lnd : erin ,
2967+ Tapd : erinTap ,
2968+ },
2969+ fabia : itestNode {
2970+ Lnd : fabia ,
2971+ Tapd : fabiaTap ,
2972+ },
2973+ yara : itestNode {
2974+ Lnd : yara ,
2975+ Tapd : yaraTap ,
2976+ },
2977+ universeTap : charlieTap ,
2978+ }
2979+
2980+ createTestMultiRFQAssetNetwork (
2981+ t , net , multiRfqNodes , cents , 10_000 , 10_000 , 10_000 ,
2982+ )
2983+
2984+ logBalance (t .t , nodes , assetID , "before multi-rfq receive" )
2985+
2986+ hodlInv := createAssetHodlInvoice (t .t , nil , fabia , 20_000 , assetID )
2987+
2988+ payInvoiceWithSatoshi (
2989+ t .t , charlie , & lnrpc.AddInvoiceResponse {
2990+ PaymentRequest : hodlInv .payReq ,
2991+ },
2992+ withGroupKey (groupID ),
2993+ withFailure (lnrpc .Payment_IN_FLIGHT , failureNone ),
2994+ )
2995+
2996+ logBalance (t .t , nodes , assetID , "after inflight multi-rfq" )
2997+
2998+ // Assert that some HTLCs are present from Fabia's point of view.
2999+ assertMinNumHtlcs (t .t , fabia , 1 )
3000+
3001+ // Assert that Charlie also has at least one outgoing HTLC as a sanity
3002+ // check.
3003+ assertMinNumHtlcs (t .t , charlie , 1 )
3004+
3005+ // Now let's cancel the invoice and assert that all inbound channels
3006+ // have cleared their HTLCs.
3007+ payHash := hodlInv .preimage .Hash ()
3008+ _ , err = fabia .InvoicesClient .CancelInvoice (
3009+ ctx , & invoicesrpc.CancelInvoiceMsg {
3010+ PaymentHash : payHash [:],
3011+ },
3012+ )
3013+ require .NoError (t .t , err )
3014+
3015+ assertNumHtlcs (t .t , dave , 0 )
3016+ assertNumHtlcs (t .t , erin , 0 )
3017+ assertNumHtlcs (t .t , yara , 0 )
3018+
3019+ logBalance (t .t , nodes , assetID , "after cancelled hodl" )
3020+
3021+ // Now let's create a normal invoice that will be settled once all the
3022+ // HTLCs have been received. This is only possible because the payer
3023+ // uses multiple bolt11 hop hints to reach the destination.
3024+ invoiceResp := createAssetInvoice (t .t , nil , fabia , 15_000 , assetID )
3025+
3026+ payInvoiceWithSatoshi (
3027+ t .t , charlie , invoiceResp , withGroupKey (groupID ),
3028+ )
3029+
3030+ logBalance (t .t , nodes , assetID , "after multi-rfq receive" )
3031+ }
3032+
28953033// testCustomChannelsStrictForwarding is a test that tests the strict forwarding
28963034// behavior of a node when it comes to paying asset invoices with assets and
28973035// BTC invoices with satoshis.
0 commit comments