Skip to content

Commit 1c9d381

Browse files
GeorgeTsagkguggero
authored andcommitted
itest: enhance multi-rfq itest with sending
1 parent 0bae92c commit 1c9d381

File tree

4 files changed

+141
-47
lines changed

4 files changed

+141
-47
lines changed

itest/assets_test.go

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,7 +1355,7 @@ func sendAssetKeySendPayment(t *testing.T, src, dst *HarnessNode, amt uint64,
13551355
return
13561356
}
13571357

1358-
result, err := getAssetPaymentResult(stream, false)
1358+
result, _, err := getAssetPaymentResult(t, stream, false)
13591359
require.NoError(t, err)
13601360
if result.Status == lnrpc.Payment_FAILED {
13611361
t.Logf("Failure reason: %v", result.FailureReason)
@@ -1719,7 +1719,13 @@ func payInvoiceWithAssets(t *testing.T, payer, rfqPeer *HarnessNode,
17191719
// was established, no network or auth error), we expect the error to be
17201720
// returned on the first read on the stream.
17211721
if cfg.errSubStr != "" {
1722-
_, err := stream.Recv()
1722+
msg, err := stream.Recv()
1723+
1724+
// On errors we still get an empty set of RFQs as a response.
1725+
if msg.GetAcceptedSellOrders() != nil {
1726+
_, err = stream.Recv()
1727+
}
1728+
17231729
require.ErrorContains(t, err, cfg.errSubStr)
17241730

17251731
return 0, rfqmath.BigIntFixedPoint{}
@@ -1729,39 +1735,18 @@ func payInvoiceWithAssets(t *testing.T, payer, rfqPeer *HarnessNode,
17291735
numUnits uint64
17301736
rateVal rfqmath.FixedPoint[rfqmath.BigInt]
17311737
)
1732-
if cfg.rfq.IsNone() {
1733-
// We want to receive the accepted quote message first, so we
1734-
// know how many assets we're going to pay.
1735-
quoteMsg, err := stream.Recv()
1736-
require.NoError(t, err)
1737-
acceptedQuote := quoteMsg.GetAcceptedSellOrder()
1738-
require.NotNil(t, acceptedQuote)
1739-
1740-
rpcRate := acceptedQuote.BidAssetRate
1741-
rate, err := rpcutils.UnmarshalRfqFixedPoint(rpcRate)
1742-
require.NoError(t, err)
17431738

1744-
rateVal = *rate
1745-
1746-
t.Logf("Got quote for %v asset units per BTC", rate)
1747-
1748-
amountMsat := lnwire.MilliSatoshi(decodedInvoice.NumMsat)
1749-
milliSatsFP := rfqmath.MilliSatoshiToUnits(amountMsat, *rate)
1750-
numUnits = milliSatsFP.ScaleTo(0).ToUint64()
1751-
msatPerUnit := float64(decodedInvoice.NumMsat) /
1752-
float64(numUnits)
1753-
t.Logf("Got quote for %v asset units at %3f msat/unit from "+
1754-
"peer %s with SCID %d", numUnits, msatPerUnit,
1755-
peerPubKey, acceptedQuote.Scid)
1756-
}
1757-
1758-
result, err := getAssetPaymentResult(
1759-
stream, cfg.payStatus == lnrpc.Payment_IN_FLIGHT,
1739+
result, rateVal, err := getAssetPaymentResult(
1740+
t, stream, cfg.payStatus == lnrpc.Payment_IN_FLIGHT,
17601741
)
17611742
require.NoError(t, err)
17621743
require.Equal(t, cfg.payStatus, result.Status)
17631744
require.Equal(t, cfg.failureReason, result.FailureReason)
17641745

1746+
amountMsat := lnwire.MilliSatoshi(decodedInvoice.NumMsat)
1747+
milliSatsFP := rfqmath.MilliSatoshiToUnits(amountMsat, rateVal)
1748+
numUnits = milliSatsFP.ScaleTo(0).ToUint64()
1749+
17651750
return numUnits, rateVal
17661751
}
17671752

itest/litd_accounts_test.go

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010

1111
"github.com/btcsuite/btcd/btcutil"
1212
"github.com/lightninglabs/lightning-terminal/litrpc"
13+
"github.com/lightninglabs/taproot-assets/rfqmath"
14+
"github.com/lightninglabs/taproot-assets/rpcutils"
1315
"github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc"
1416
"github.com/lightningnetwork/lnd/lnrpc"
1517
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
@@ -443,43 +445,75 @@ func getPaymentResult(stream routerrpc.Router_SendPaymentV2Client,
443445
}
444446
}
445447

446-
func getAssetPaymentResult(
448+
func getAssetPaymentResult(t *testing.T,
447449
s tapchannelrpc.TaprootAssetChannels_SendPaymentClient,
448-
isHodl bool) (*lnrpc.Payment, error) {
450+
isHodl bool) (*lnrpc.Payment, rfqmath.FixedPoint[rfqmath.BigInt],
451+
error) {
449452

450453
// No idea why it makes a difference whether we wait before calling
451454
// s.Recv() or not, but it does. Without the sleep, the test will fail
452455
// with "insufficient local balance"... ¯\_(ツ)_/¯
453456
// Probably something weird within lnd itself.
454457
time.Sleep(time.Second)
455458

459+
var rateVal rfqmath.FixedPoint[rfqmath.BigInt]
460+
456461
for {
457462
msg, err := s.Recv()
458463
if err != nil {
459-
return nil, err
464+
return nil, rateVal, err
460465
}
461466

462467
// Ignore RFQ quote acceptance messages read from the send
463468
// payment stream, as they are not relevant.
464469
quote := msg.GetAcceptedSellOrder()
465470
if quote != nil {
471+
rpcRate := quote.BidAssetRate
472+
rate, err := rpcutils.UnmarshalRfqFixedPoint(rpcRate)
473+
require.NoError(t, err)
474+
475+
rateVal = *rate
476+
477+
t.Logf("Got quote for %v asset units per BTC from "+
478+
"peer %v", rate, quote.Peer)
479+
continue
480+
}
481+
482+
// Ignore the new RFQ array message from the stream, it is also
483+
// not relevant.
484+
quotes := msg.GetAcceptedSellOrders()
485+
if quotes != nil {
486+
for _, quote := range quotes.AcceptedSellOrders {
487+
rpcRate := quote.BidAssetRate
488+
rate, err := rpcutils.UnmarshalRfqFixedPoint(
489+
rpcRate,
490+
)
491+
require.NoError(t, err)
492+
493+
rateVal = *rate
494+
495+
t.Logf("Got quote for %v asset units per BTC "+
496+
"from peer %v", rate, quote.Peer)
497+
}
498+
466499
continue
467500
}
468501

469502
payment := msg.GetPaymentResult()
470503
if payment == nil {
471-
return nil, fmt.Errorf("unexpected message: %v", msg)
504+
return nil, rateVal,
505+
fmt.Errorf("unexpected message: %v", msg)
472506
}
473507

474508
// If this is a hodl payment, then we'll return the first
475509
// expected response. Otherwise, we'll wait until the in flight
476510
// clears to we can observe the other payment states.
477511
switch {
478512
case isHodl:
479-
return payment, nil
513+
return payment, rateVal, nil
480514

481515
case payment.Status != lnrpc.Payment_IN_FLIGHT:
482-
return payment, nil
516+
return payment, rateVal, nil
483517
}
484518
}
485519
}

itest/litd_custom_channels_test.go

Lines changed: 86 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2613,9 +2613,9 @@ func testCustomChannelsLiquidityEdgeCasesCore(ctx context.Context,
26132613

26142614
payInvoiceWithAssets(
26152615
t.t, charlie, dave, btcInvoiceResp.PaymentRequest, assetID,
2616-
withFeeLimit(2_000), withPayErrSubStr(
2617-
"rejecting payment of 20000 mSAT",
2618-
), withGroupKey(groupID),
2616+
withFeeLimit(2_000), withGroupKey(groupID), withPayErrSubStr(
2617+
"failed to acquire any quotes",
2618+
),
26192619
)
26202620

26212621
// When we override the uneconomical payment, it should succeed.
@@ -2640,7 +2640,7 @@ func testCustomChannelsLiquidityEdgeCasesCore(ctx context.Context,
26402640
payInvoiceWithAssets(
26412641
t.t, charlie, dave, btcInvoiceResp.PaymentRequest, assetID,
26422642
withFeeLimit(1_000), withAllowOverpay(), withPayErrSubStr(
2643-
"rejecting payment of 2000 mSAT",
2643+
"failed to acquire any quotes",
26442644
), withGroupKey(groupID),
26452645
)
26462646

@@ -2971,10 +2971,11 @@ func testCustomChannelsLiquidityEdgeCasesGroup(ctx context.Context,
29712971
testCustomChannelsLiquidityEdgeCasesCore(ctx, net, t, true)
29722972
}
29732973

2974-
// testCustomChannelsMultiRFQReceive tests that a node creating an invoice with
2975-
// multiple RFQ quotes can actually guide the payer into using multiple private
2976-
// taproot asset channels to pay the invoice.
2977-
func testCustomChannelsMultiRFQReceive(ctx context.Context, net *NetworkHarness,
2974+
// testCustomChannelsMultiRFQ tests that sending and receiving payments works
2975+
// when using the multi-rfq features of tapd. This means that liquidity across
2976+
// multiple channels and peers can be used to send out a payment, or receive to
2977+
// an invoice.
2978+
func testCustomChannelsMultiRFQ(ctx context.Context, net *NetworkHarness,
29782979
t *harnessTest) {
29792980

29802981
lndArgs := slices.Clone(lndArgsTemplate)
@@ -3068,7 +3069,6 @@ func testCustomChannelsMultiRFQReceive(ctx context.Context, net *NetworkHarness,
30683069
t.t, charlie, &lnrpc.AddInvoiceResponse{
30693070
PaymentRequest: hodlInv.payReq,
30703071
},
3071-
withGroupKey(groupID),
30723072
withFailure(lnrpc.Payment_IN_FLIGHT, failureNone),
30733073
)
30743074

@@ -3100,13 +3100,88 @@ func testCustomChannelsMultiRFQReceive(ctx context.Context, net *NetworkHarness,
31003100
// Now let's create a normal invoice that will be settled once all the
31013101
// HTLCs have been received. This is only possible because the payer
31023102
// uses multiple bolt11 hop hints to reach the destination.
3103-
invoiceResp := createAssetInvoice(t.t, nil, fabia, 15_000, assetID)
3103+
invoiceResp := createAssetInvoice(
3104+
t.t, nil, fabia, 15_000, nil, withInvGroupKey(groupID),
3105+
)
31043106

31053107
payInvoiceWithSatoshi(
3106-
t.t, charlie, invoiceResp, withGroupKey(groupID),
3108+
t.t, charlie, invoiceResp,
31073109
)
31083110

31093111
logBalance(t.t, nodes, assetID, "after multi-rfq receive")
3112+
3113+
// Now we'll test that sending with multiple rfq quotes works.
3114+
3115+
// Let's start by providing some liquidity to Charlie's peers, in order
3116+
// for them to be able to push some amount if Fabia picks them as part
3117+
// of the route.
3118+
sendKeySendPayment(t.t, charlie, erin, 800_000)
3119+
sendKeySendPayment(t.t, charlie, dave, 800_000)
3120+
sendKeySendPayment(t.t, charlie, yara, 800_000)
3121+
3122+
// Let's ask for the rough equivalent of ~15k assets. Fabia, who's going
3123+
// to pay the invoice, only has parts of assets that are less than 10k
3124+
// in channels with one of the 3 intermediate peers. The only way to
3125+
// pay this invoice is by splitting the payment across multiple peers by
3126+
// using multiple RFQ quotes.
3127+
invAmt := int64(15_000 * 17)
3128+
3129+
iResp, err := charlie.AddHoldInvoice(
3130+
ctx, &invoicesrpc.AddHoldInvoiceRequest{
3131+
Memo: "",
3132+
Value: invAmt,
3133+
Hash: payHash[:],
3134+
},
3135+
)
3136+
require.NoError(t.t, err)
3137+
3138+
payReq := iResp.PaymentRequest
3139+
3140+
payInvoiceWithAssets(
3141+
t.t, fabia, nil, payReq, assetID,
3142+
withFailure(lnrpc.Payment_IN_FLIGHT, failureNone),
3143+
)
3144+
3145+
assertMinNumHtlcs(t.t, charlie, 2)
3146+
assertMinNumHtlcs(t.t, fabia, 2)
3147+
3148+
logBalance(t.t, nodes, assetID, "multi-rfq send in-flight")
3149+
3150+
_, err = charlie.SettleInvoice(ctx, &invoicesrpc.SettleInvoiceMsg{
3151+
Preimage: hodlInv.preimage[:],
3152+
})
3153+
require.NoError(t.t, err)
3154+
3155+
assertNumHtlcs(t.t, charlie, 0)
3156+
assertNumHtlcs(t.t, fabia, 0)
3157+
3158+
logBalance(t.t, nodes, assetID, "after multi-rfq send")
3159+
3160+
// Let's make another round-trip involving multi-rfq functionality.
3161+
// Let's have Fabia receive another large payment and send it back
3162+
// again, this time with a greater amount.
3163+
invoiceResp = createAssetInvoice(t.t, nil, fabia, 25_000, assetID)
3164+
3165+
payInvoiceWithSatoshi(
3166+
t.t, charlie, invoiceResp,
3167+
)
3168+
3169+
logBalance(t.t, nodes, assetID, "after multi-rfq receive (2nd)")
3170+
3171+
// Let's bump up the invoice amount a bit, to roughly ~22k assets.
3172+
invAmt = 22_000 * 17
3173+
inv, err := charlie.AddInvoice(ctx, &lnrpc.Invoice{
3174+
Value: invAmt,
3175+
})
3176+
require.NoError(t.t, err)
3177+
3178+
payReq = inv.PaymentRequest
3179+
3180+
payInvoiceWithAssets(
3181+
t.t, fabia, nil, payReq, nil, withGroupKey(groupID),
3182+
)
3183+
3184+
logBalance(t.t, nodes, assetID, "after multi-rfq send (2nd)")
31103185
}
31113186

31123187
// testCustomChannelsStrictForwarding is a test that tests the strict forwarding

itest/litd_test_list_on_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ var allTestCases = []*testCase{
130130
},
131131
{
132132
name: "custom channels multi rfq",
133-
test: testCustomChannelsMultiRFQReceive,
133+
test: testCustomChannelsMultiRFQ,
134134
noAliceBob: true,
135135
},
136136
{

0 commit comments

Comments
 (0)