@@ -2026,32 +2026,6 @@ func testCustomChannelsLiquidityEdgeCases(ctx context.Context,
20262026 ),
20272027 )
20282028
2029- // Edge case: Now Dave creates an asset invoice to be paid for by
2030- // Yara with satoshi. For the last hop we try to settle the invoice in
2031- // satoshi, where we will check whether Dave's strict forwarding works
2032- // as expected. Charlie is only used as a dummy RFQ peer in this case,
2033- // Yara totally ignored the RFQ hint and pays agnostically with sats.
2034- invoiceResp = createAssetInvoice (t .t , charlie , dave , 22 , assetID )
2035-
2036- stream , err := dave .InvoicesClient .SubscribeSingleInvoice (
2037- ctx , & invoicesrpc.SubscribeSingleInvoiceRequest {
2038- RHash : invoiceResp .RHash ,
2039- },
2040- )
2041- require .NoError (t .t , err )
2042-
2043- // Yara pays Dave with enough satoshis, but Charlie will not settle as
2044- // he expects assets.
2045- hops := [][]byte {dave .PubKey [:]}
2046- payInvoiceWithSatoshiLastHop (t .t , yara , invoiceResp , hops , withFailure (
2047- lnrpc .Payment_FAILED , 0 ,
2048- ))
2049-
2050- t .lndHarness .LNDHarness .AssertInvoiceState (stream , lnrpc .Invoice_OPEN )
2051-
2052- logBalance (t .t , nodes , assetID , "after failed payment (asset " +
2053- "invoice, strict forwarding)" )
2054-
20552029 // Edge case: Check if the RFQ HTLC tracking accounts for cancelled
20562030 // HTLCs. We achieve this by manually creating & using an RFQ quote with
20572031 // a set max amount. We first pay to a hodl invoice that we eventually
@@ -2192,6 +2166,221 @@ func testCustomChannelsLiquidityEdgeCases(ctx context.Context,
21922166 logBalance (t .t , nodes , assetID , "after small manual rfq" )
21932167}
21942168
2169+ // testCustomChannelsStrictForwarding is a test that tests the strict forwarding
2170+ // behavior of a node when it comes to paying asset invoices with assets and
2171+ // BTC invoices with satoshis.
2172+ func testCustomChannelsStrictForwarding (ctx context.Context ,
2173+ net * NetworkHarness , t * harnessTest ) {
2174+
2175+ lndArgs := slices .Clone (lndArgsTemplate )
2176+ litdArgs := slices .Clone (litdArgsTemplate )
2177+
2178+ // Explicitly set the proof courier as Zane (now has no other role
2179+ // other than proof shuffling), otherwise a hashmail courier will be
2180+ // used. For the funding transaction, we're just posting it and don't
2181+ // expect a true receiver.
2182+ zane , err := net .NewNode (
2183+ t .t , "Zane" , lndArgs , false , true , litdArgs ... ,
2184+ )
2185+ require .NoError (t .t , err )
2186+
2187+ litdArgs = append (litdArgs , fmt .Sprintf (
2188+ "--taproot-assets.proofcourieraddr=%s://%s" ,
2189+ proof .UniverseRpcCourierType , zane .Cfg .LitAddr (),
2190+ ))
2191+
2192+ // The topology we are going for looks like the following:
2193+ //
2194+ // Charlie --[assets]--> Dave --[sats]--> Erin --[assets]--> Fabia
2195+ // |
2196+ // |
2197+ // [assets]
2198+ // |
2199+ // v
2200+ // Yara
2201+ //
2202+ // With [assets] being a custom channel and [sats] being a normal, BTC
2203+ // only channel.
2204+ // All 5 nodes need to be full litd nodes running in integrated mode
2205+ // with tapd included. We also need specific flags to be enabled, so we
2206+ // create 5 completely new nodes, ignoring the two default nodes that
2207+ // are created by the harness.
2208+ charlie , err := net .NewNode (
2209+ t .t , "Charlie" , lndArgs , false , true , litdArgs ... ,
2210+ )
2211+ require .NoError (t .t , err )
2212+
2213+ dave , err := net .NewNode (t .t , "Dave" , lndArgs , false , true , litdArgs ... )
2214+ require .NoError (t .t , err )
2215+ erin , err := net .NewNode (t .t , "Erin" , lndArgs , false , true , litdArgs ... )
2216+ require .NoError (t .t , err )
2217+ fabia , err := net .NewNode (
2218+ t .t , "Fabia" , lndArgs , false , true , litdArgs ... ,
2219+ )
2220+ require .NoError (t .t , err )
2221+ yara , err := net .NewNode (
2222+ t .t , "Yara" , lndArgs , false , true , litdArgs ... ,
2223+ )
2224+ require .NoError (t .t , err )
2225+
2226+ nodes := []* HarnessNode {charlie , dave , erin , fabia , yara }
2227+ connectAllNodes (t .t , net , nodes )
2228+ fundAllNodes (t .t , net , nodes )
2229+
2230+ // Create the normal channel between Dave and Erin.
2231+ t .Logf ("Opening normal channel between Dave and Erin..." )
2232+ channelOp := openChannelAndAssert (
2233+ t , net , dave , erin , lntest.OpenChannelParams {
2234+ Amt : 10_000_000 ,
2235+ SatPerVByte : 5 ,
2236+ },
2237+ )
2238+ defer closeChannelAndAssert (t , net , dave , channelOp , true )
2239+
2240+ // This is the only public channel, we need everyone to be aware of it.
2241+ assertChannelKnown (t .t , charlie , channelOp )
2242+ assertChannelKnown (t .t , fabia , channelOp )
2243+
2244+ universeTap := newTapClient (t .t , zane )
2245+ charlieTap := newTapClient (t .t , charlie )
2246+ daveTap := newTapClient (t .t , dave )
2247+ erinTap := newTapClient (t .t , erin )
2248+ fabiaTap := newTapClient (t .t , fabia )
2249+ yaraTap := newTapClient (t .t , yara )
2250+
2251+ // Mint an asset on Charlie and sync all nodes to Charlie as the
2252+ // universe.
2253+ mintedAssets := itest .MintAssetsConfirmBatch (
2254+ t .t , t .lndHarness .Miner .Client , charlieTap ,
2255+ []* mintrpc.MintAssetRequest {
2256+ {
2257+ Asset : itestAsset ,
2258+ },
2259+ },
2260+ )
2261+ cents := mintedAssets [0 ]
2262+ assetID := cents .AssetGenesis .AssetId
2263+
2264+ t .Logf ("Minted %d lightning cents, syncing universes..." , cents .Amount )
2265+ syncUniverses (t .t , charlieTap , dave , erin , fabia , yara )
2266+ t .Logf ("Universes synced between all nodes, distributing assets..." )
2267+
2268+ const (
2269+ daveFundingAmount = uint64 (400_000 )
2270+ erinFundingAmount = uint64 (200_000 )
2271+ )
2272+ charlieFundingAmount := cents .Amount - uint64 (2 * 400_000 )
2273+
2274+ _ , _ , _ = createTestAssetNetwork (
2275+ t , net , charlieTap , daveTap , erinTap , fabiaTap , yaraTap ,
2276+ universeTap , cents , 400_000 , charlieFundingAmount ,
2277+ daveFundingAmount , erinFundingAmount , 0 ,
2278+ )
2279+
2280+ // Before we start sending out payments, let's make sure each node can
2281+ // see the other one in the graph and has all required features.
2282+ require .NoError (t .t , t .lndHarness .AssertNodeKnown (charlie , dave ))
2283+ require .NoError (t .t , t .lndHarness .AssertNodeKnown (dave , charlie ))
2284+ require .NoError (t .t , t .lndHarness .AssertNodeKnown (dave , yara ))
2285+ require .NoError (t .t , t .lndHarness .AssertNodeKnown (yara , dave ))
2286+ require .NoError (t .t , t .lndHarness .AssertNodeKnown (erin , fabia ))
2287+ require .NoError (t .t , t .lndHarness .AssertNodeKnown (fabia , erin ))
2288+ require .NoError (t .t , t .lndHarness .AssertNodeKnown (charlie , erin ))
2289+
2290+ logBalance (t .t , nodes , assetID , "initial" )
2291+
2292+ // Do a payment from Charlie to Erin to shift the balances in all
2293+ // channels enough to allow for the following payments in any direction.
2294+ // Pay a normal bolt11 invoice involving RFQ flow.
2295+ _ = createAndPayNormalInvoice (
2296+ t .t , charlie , dave , erin , 500_000 , assetID , withSmallShards (),
2297+ )
2298+
2299+ logBalance (t .t , nodes , assetID , "after payment" )
2300+
2301+ // Edge case: Now Dave creates an asset invoice to be paid for by Erin
2302+ // with satoshi. For the last hop we try to settle the invoice in
2303+ // satoshi, where we will check whether Daves's strict forwarding
2304+ // works as expected. Charlie is only used as a dummy RFQ peer in this
2305+ // case, Erin totally ignores the RFQ hint and just pays with sats.
2306+ assetInvoice := createAssetInvoice (t .t , charlie , dave , 40 , assetID )
2307+
2308+ assetInvoiceStream , err := dave .InvoicesClient .SubscribeSingleInvoice (
2309+ ctx , & invoicesrpc.SubscribeSingleInvoiceRequest {
2310+ RHash : assetInvoice .RHash ,
2311+ },
2312+ )
2313+ require .NoError (t .t , err )
2314+
2315+ // Erin pays Dave with enough satoshis, but Charlie will not settle as
2316+ // he expects assets.
2317+ hops := [][]byte {dave .PubKey [:]}
2318+ payInvoiceWithSatoshiLastHop (
2319+ t .t , erin , assetInvoice , hops , withFailure (
2320+ lnrpc .Payment_FAILED , 0 ,
2321+ ),
2322+ )
2323+
2324+ // Make sure the invoice hasn't been settled and there's no HTLC on the
2325+ // channel between Erin and Dave.
2326+ t .lndHarness .LNDHarness .AssertInvoiceState (
2327+ assetInvoiceStream , lnrpc .Invoice_OPEN ,
2328+ )
2329+ assertHTLCNotActive (t .t , erin , channelOp , assetInvoice .RHash )
2330+ assertInvoiceState (
2331+ t .t , dave , assetInvoice .PaymentAddr , lnrpc .Invoice_OPEN ,
2332+ )
2333+
2334+ logBalance (t .t , nodes , assetID , "after failed payment (asset " +
2335+ "invoice, strict forwarding)" )
2336+
2337+ // Now let's make sure that we can actually still pay the invoice with
2338+ // assets from Charlie.
2339+ payInvoiceWithAssets (
2340+ t .t , charlie , dave , assetInvoice .PaymentRequest , assetID ,
2341+ )
2342+ t .lndHarness .LNDHarness .AssertInvoiceState (
2343+ assetInvoiceStream , lnrpc .Invoice_SETTLED ,
2344+ )
2345+ assertInvoiceState (
2346+ t .t , dave , assetInvoice .PaymentAddr , lnrpc .Invoice_SETTLED ,
2347+ )
2348+
2349+ // Edge case: We now try the opposite: Dave creates a BTC invoice but
2350+ // Charlie tries to pay it with assets. This should fail as well.
2351+ btcInvoice := createNormalInvoice (t .t , dave , 1_000 )
2352+ btcInvoiceStream , err := dave .InvoicesClient .SubscribeSingleInvoice (
2353+ ctx , & invoicesrpc.SubscribeSingleInvoiceRequest {
2354+ RHash : btcInvoice .RHash ,
2355+ },
2356+ )
2357+ require .NoError (t .t , err )
2358+
2359+ payInvoiceWithAssets (
2360+ t .t , charlie , dave , btcInvoice .PaymentRequest , assetID ,
2361+ withFailure (lnrpc .Payment_FAILED , failureIncorrectDetails ),
2362+ )
2363+ t .lndHarness .LNDHarness .AssertInvoiceState (
2364+ btcInvoiceStream , lnrpc .Invoice_OPEN ,
2365+ )
2366+ assertHTLCNotActive (t .t , erin , channelOp , btcInvoice .RHash )
2367+ assertInvoiceState (
2368+ t .t , dave , btcInvoice .PaymentAddr , lnrpc .Invoice_OPEN ,
2369+ )
2370+
2371+ // And finally we make sure that we can still pay the invoice with
2372+ // satoshis from Erin, using custom records.
2373+ payInvoiceWithSatoshi (t .t , erin , btcInvoice , withDestCustomRecords (
2374+ map [uint64 ][]byte {106823 : {0x01 }},
2375+ ))
2376+ t .lndHarness .LNDHarness .AssertInvoiceState (
2377+ btcInvoiceStream , lnrpc .Invoice_SETTLED ,
2378+ )
2379+ assertInvoiceState (
2380+ t .t , dave , btcInvoice .PaymentAddr , lnrpc .Invoice_SETTLED ,
2381+ )
2382+ }
2383+
21952384// testCustomChannelsBalanceConsistency is a test that test the balance of nodes
21962385// under channel opening circumstances.
21972386func testCustomChannelsBalanceConsistency (ctx context.Context ,
0 commit comments