@@ -2026,32 +2026,6 @@ func testCustomChannelsLiquidityEdgeCases(ctx context.Context,
2026
2026
),
2027
2027
)
2028
2028
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
-
2055
2029
// Edge case: Check if the RFQ HTLC tracking accounts for cancelled
2056
2030
// HTLCs. We achieve this by manually creating & using an RFQ quote with
2057
2031
// a set max amount. We first pay to a hodl invoice that we eventually
@@ -2192,6 +2166,221 @@ func testCustomChannelsLiquidityEdgeCases(ctx context.Context,
2192
2166
logBalance (t .t , nodes , assetID , "after small manual rfq" )
2193
2167
}
2194
2168
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
+
2195
2384
// testCustomChannelsBalanceConsistency is a test that test the balance of nodes
2196
2385
// under channel opening circumstances.
2197
2386
func testCustomChannelsBalanceConsistency (ctx context.Context ,
0 commit comments