11package itest
22
33import (
4+ "bytes"
45 "context"
56 "fmt"
67 "math"
@@ -18,12 +19,14 @@ import (
1819 "github.com/lightninglabs/taproot-assets/tapchannel"
1920 "github.com/lightninglabs/taproot-assets/taprpc"
2021 "github.com/lightninglabs/taproot-assets/taprpc/mintrpc"
22+ "github.com/lightninglabs/taproot-assets/taprpc/rfqrpc"
2123 tchrpc "github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc"
2224 "github.com/lightninglabs/taproot-assets/taprpc/universerpc"
2325 "github.com/lightninglabs/taproot-assets/tapscript"
2426 "github.com/lightningnetwork/lnd/fn"
2527 "github.com/lightningnetwork/lnd/lnrpc"
2628 "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
29+ "github.com/lightningnetwork/lnd/lnrpc/routerrpc"
2730 "github.com/lightningnetwork/lnd/lnrpc/walletrpc"
2831 "github.com/lightningnetwork/lnd/lntest"
2932 "github.com/lightningnetwork/lnd/lntest/port"
@@ -1805,7 +1808,7 @@ func testCustomChannelsLiquidityEdgeCases(_ context.Context,
18051808 SatPerVByte : 5 ,
18061809 },
18071810 )
1808- defer closeChannelAndAssert (t , net , dave , channelOp , false )
1811+ defer closeChannelAndAssert (t , net , dave , channelOp , true )
18091812
18101813 // This is the only public channel, we need everyone to be aware of it.
18111814 assertChannelKnown (t .t , charlie , channelOp )
@@ -2020,10 +2023,11 @@ func testCustomChannelsLiquidityEdgeCases(_ context.Context,
20202023 logBalance (t .t , nodes , assetID , "after small payment (asset " +
20212024 "invoice, <354sats)" )
20222025
2023- // Edge case: Now Charlie creates an asset invoice to be paid for by
2026+ // Edge case: Now Dave creates an asset invoice to be paid for by
20242027 // Yara with satoshi. For the last hop we try to settle the invoice in
2025- // satoshi, where we will check whether Charlie's strict forwarding
2026- // works as expected.
2028+ // satoshi, where we will check whether Dave's strict forwarding works
2029+ // as expected. Charlie is only used as a dummy RFQ peer in this case,
2030+ // Yara totally ignored the RFQ hint and pays agnostically with sats.
20272031 invoiceResp = createAssetInvoice (
20282032 t .t , charlie , dave , 1 , assetID ,
20292033 )
@@ -2046,6 +2050,91 @@ func testCustomChannelsLiquidityEdgeCases(_ context.Context,
20462050
20472051 logBalance (t .t , nodes , assetID , "after failed payment (asset " +
20482052 "invoice, strict forwarding)" )
2053+
2054+ // Edge case: Charlie negotiates a quote with Dave which has a low max
2055+ // amount (~170k sats). Then Charlie creates an invoice with a total
2056+ // amount slightly larger than the max allowed in the quote (200k sats).
2057+ // Erin will try to pay that invoice with sats, in shards of max size
2058+ // 80k sats. Dave will eventually stop forwarding HTLCs as the RFQ HTLC
2059+ // tracking mechanism should stop them from being forwarded, as they
2060+ // violate the maximum allowed amount of the quote.
2061+
2062+ // Charlie starts by negotiating the quote.
2063+ res , err := charlieTap .RfqClient .AddAssetBuyOrder (
2064+ ctxb , & rfqrpc.AddAssetBuyOrderRequest {
2065+ AssetSpecifier : & rfqrpc.AssetSpecifier {
2066+ Id : & rfqrpc.AssetSpecifier_AssetId {
2067+ AssetId : assetID ,
2068+ },
2069+ },
2070+ AssetMaxAmt : 10_000 ,
2071+ Expiry : uint64 (time .Now ().Add (time .Hour ).Unix ()),
2072+ PeerPubKey : dave .PubKey [:],
2073+ TimeoutSeconds : 10 ,
2074+ },
2075+ )
2076+ require .NoError (t .t , err )
2077+
2078+ quote , ok := res .Response .(* rfqrpc.AddAssetBuyOrderResponse_AcceptedQuote )
2079+ require .True (t .t , ok )
2080+
2081+ // We now manually add the invoice in order to inject the above,
2082+ // manually generated, quote.
2083+ iResp , err := charlie .AddInvoice (ctxb , & lnrpc.Invoice {
2084+ Memo : "" ,
2085+ Value : 200_000 ,
2086+ RPreimage : bytes .Repeat ([]byte {11 }, 32 ),
2087+ CltvExpiry : 60 ,
2088+ RouteHints : []* lnrpc.RouteHint {
2089+ & lnrpc.RouteHint {
2090+ HopHints : []* lnrpc.HopHint {
2091+ & lnrpc.HopHint {
2092+ NodeId : dave .PubKeyStr ,
2093+ ChanId : quote .AcceptedQuote .Scid ,
2094+ },
2095+ },
2096+ },
2097+ },
2098+ })
2099+ require .NoError (t .t , err )
2100+
2101+ // Now Erin tries to pay the invoice. Since the multipart payment will
2102+ // have some of its shards failing the pathfinding logic will keep going
2103+ // and we won't see a payment failure but a timeout. If a final outcome
2104+ // is not produced within a reasonable amount of time, we assume the
2105+ // payment is still trying to find a route, therefore the HTLC rejection
2106+ // works.
2107+ timeoutChan = time .After (PaymentTimeout / 2 )
2108+ done = make (chan bool , 1 )
2109+
2110+ ctxc , cancel := context .WithCancel (context .Background ())
2111+
2112+ //nolint:lll
2113+ go func () {
2114+ // payInvoiceWithSatoshi(t.t, erin, iResp, lnrpc.Payment_FAILED)
2115+ sendReq := & routerrpc.SendPaymentRequest {
2116+ PaymentRequest : iResp .PaymentRequest ,
2117+ TimeoutSeconds : int32 (PaymentTimeout .Seconds ()),
2118+ MaxShardSizeMsat : 80_000_000 ,
2119+ FeeLimitMsat : 1_000_000 ,
2120+ }
2121+ stream , err := erin .RouterClient .SendPaymentV2 (ctxc , sendReq )
2122+ if err == nil {
2123+ _ , _ = getPaymentResult (stream )
2124+ }
2125+
2126+ done <- true
2127+ }()
2128+
2129+ select {
2130+ case <- done :
2131+ t .Fatalf ("Payment should not produce a final outcome" )
2132+
2133+ case <- timeoutChan :
2134+ cancel ()
2135+ }
2136+
2137+ logBalance (t .t , nodes , assetID , "after htlc track" )
20492138}
20502139
20512140// testCustomChannelsBalanceConsistency is a test that test the balance of nodes
0 commit comments