@@ -2745,6 +2745,160 @@ func testCustomChannelsLiquidityEdgeCasesGroup(ctx context.Context,
2745
2745
testCustomChannelsLiquidtyEdgeCasesCore (ctx , net , t , true )
2746
2746
}
2747
2747
2748
+ // testCustomChannelsMultiRFQReceive tests that a node creating an invoice with
2749
+ // multiple RFQ quotes can actually guide the payer into using multiple private
2750
+ // taproot asset channels to pay the invoice.
2751
+ func testCustomChannelsMultiRFQReceive (ctx context.Context , net * NetworkHarness ,
2752
+ t * harnessTest ) {
2753
+
2754
+ lndArgs := slices .Clone (lndArgsTemplate )
2755
+ litdArgs := slices .Clone (litdArgsTemplate )
2756
+
2757
+ // Explicitly set the proof courier as Zane (now has no other role
2758
+ // other than proof shuffling), otherwise a hashmail courier will be
2759
+ // used. For the funding transaction, we're just posting it and don't
2760
+ // expect a true receiver.
2761
+ zane , err := net .NewNode (
2762
+ t .t , "Zane" , lndArgs , false , true , litdArgs ... ,
2763
+ )
2764
+ require .NoError (t .t , err )
2765
+
2766
+ litdArgs = append (litdArgs , fmt .Sprintf (
2767
+ "--taproot-assets.proofcourieraddr=%s://%s" ,
2768
+ proof .UniverseRpcCourierType , zane .Cfg .LitAddr (),
2769
+ ))
2770
+
2771
+ charlie , err := net .NewNode (
2772
+ t .t , "Charlie" , lndArgs , false , true , litdArgs ... ,
2773
+ )
2774
+ require .NoError (t .t , err )
2775
+
2776
+ dave , err := net .NewNode (t .t , "Dave" , lndArgs , false , true , litdArgs ... )
2777
+ require .NoError (t .t , err )
2778
+ erin , err := net .NewNode (t .t , "Erin" , lndArgs , false , true , litdArgs ... )
2779
+ require .NoError (t .t , err )
2780
+ fabia , err := net .NewNode (
2781
+ t .t , "Fabia" , lndArgs , false , true , litdArgs ... ,
2782
+ )
2783
+ require .NoError (t .t , err )
2784
+ yara , err := net .NewNode (
2785
+ t .t , "Yara" , lndArgs , false , true , litdArgs ... ,
2786
+ )
2787
+ require .NoError (t .t , err )
2788
+
2789
+ nodes := []* HarnessNode {charlie , dave , erin , fabia , yara }
2790
+ connectAllNodes (t .t , net , nodes )
2791
+ fundAllNodes (t .t , net , nodes )
2792
+
2793
+ // The topology we are going for looks like the following:
2794
+ //
2795
+ // /---[sats]--> Erin --[assets]--\
2796
+ // / \
2797
+ // / \
2798
+ // Charlie -----[sats]--> Dave --[assets]---->Fabia
2799
+ // \ /
2800
+ // \ /
2801
+ // \---[sats]--> Yara --[assets]--/
2802
+ //
2803
+
2804
+ // Let's open the normal sats channels between Charlie and the routing
2805
+ // peers.
2806
+ _ = openChannelAndAssert (
2807
+ t , net , charlie , erin , lntest.OpenChannelParams {
2808
+ Amt : 10_000_000 ,
2809
+ SatPerVByte : 5 ,
2810
+ },
2811
+ )
2812
+
2813
+ _ = openChannelAndAssert (
2814
+ t , net , charlie , dave , lntest.OpenChannelParams {
2815
+ Amt : 10_000_000 ,
2816
+ SatPerVByte : 5 ,
2817
+ },
2818
+ )
2819
+
2820
+ _ = openChannelAndAssert (
2821
+ t , net , charlie , yara , lntest.OpenChannelParams {
2822
+ Amt : 10_000_000 ,
2823
+ SatPerVByte : 5 ,
2824
+ },
2825
+ )
2826
+
2827
+ // Let's create the tap clients.
2828
+ universeTap := newTapClient (t .t , zane )
2829
+ charlieTap := newTapClient (t .t , charlie )
2830
+ daveTap := newTapClient (t .t , dave )
2831
+ erinTap := newTapClient (t .t , erin )
2832
+ fabiaTap := newTapClient (t .t , fabia )
2833
+ yaraTap := newTapClient (t .t , yara )
2834
+
2835
+ assetReq := itest .CopyRequest (& mintrpc.MintAssetRequest {
2836
+ Asset : itestAsset ,
2837
+ })
2838
+
2839
+ assetReq .Asset .NewGroupedAsset = true
2840
+
2841
+ // Mint an asset on Charlie and sync all nodes to Charlie as the
2842
+ // universe.
2843
+ mintedAssets := itest .MintAssetsConfirmBatch (
2844
+ t .t , t .lndHarness .Miner .Client , charlieTap ,
2845
+ []* mintrpc.MintAssetRequest {assetReq },
2846
+ )
2847
+ cents := mintedAssets [0 ]
2848
+ assetID := cents .AssetGenesis .AssetId
2849
+ groupID := cents .GetAssetGroup ().GetTweakedGroupKey ()
2850
+
2851
+ syncUniverses (t .t , charlieTap , dave , erin , fabia , yara )
2852
+
2853
+ createTestMultiRFQAssetNetwork (
2854
+ t , net , charlieTap , daveTap , erinTap , fabiaTap , yaraTap ,
2855
+ universeTap , cents , 10_000 , 10_000 , 10_000 ,
2856
+ )
2857
+
2858
+ logBalance (t .t , nodes , assetID , "before multi-rfq receive" )
2859
+
2860
+ hodlInv := createAssetHodlInvoice (t .t , nil , fabia , 20_000 , assetID )
2861
+
2862
+ payInvoiceWithSatoshi (
2863
+ t .t , charlie , & lnrpc.AddInvoiceResponse {
2864
+ PaymentRequest : hodlInv .payReq ,
2865
+ },
2866
+ withGroupKey (groupID ),
2867
+ withFailure (lnrpc .Payment_IN_FLIGHT , failureNone ),
2868
+ )
2869
+
2870
+ logBalance (t .t , nodes , assetID , "after inflight multi-rfq" )
2871
+
2872
+ // TODO: assert minNumHtlcs after rebase
2873
+
2874
+ // Now let's cancel the invoice and assert that all inbound channels
2875
+ // have cleared their HTLCs.
2876
+ payHash := hodlInv .preimage .Hash ()
2877
+ _ , err = fabia .InvoicesClient .CancelInvoice (
2878
+ ctx , & invoicesrpc.CancelInvoiceMsg {
2879
+ PaymentHash : payHash [:],
2880
+ },
2881
+ )
2882
+ require .NoError (t .t , err )
2883
+
2884
+ assertNumHtlcs (t .t , dave , 0 )
2885
+ assertNumHtlcs (t .t , erin , 0 )
2886
+ assertNumHtlcs (t .t , yara , 0 )
2887
+
2888
+ logBalance (t .t , nodes , assetID , "after cancelled hodl" )
2889
+
2890
+ // Now let's create a normal invoice that will be settled once all the
2891
+ // HTLCs have been received. This is only possible because the payer
2892
+ // uses multiple bolt11 hop hints to reach the destination.
2893
+ invoiceResp := createAssetInvoice (t .t , nil , fabia , 15_000 , assetID )
2894
+
2895
+ payInvoiceWithSatoshi (
2896
+ t .t , charlie , invoiceResp , withGroupKey (groupID ),
2897
+ )
2898
+
2899
+ logBalance (t .t , nodes , assetID , "after multi-rfq receive" )
2900
+ }
2901
+
2748
2902
// testCustomChannelsStrictForwarding is a test that tests the strict forwarding
2749
2903
// behavior of a node when it comes to paying asset invoices with assets and
2750
2904
// BTC invoices with satoshis.
0 commit comments