From 33f4ef3872da8cd5efd8b42ed3e0a544789a2aa4 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 4 Jun 2025 11:03:16 +0200 Subject: [PATCH] itest: add assets to btc self-payment test case --- itest/assets_test.go | 52 +++++++++++++++------ itest/litd_custom_channels_test.go | 74 ++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 14 deletions(-) diff --git a/itest/assets_test.go b/itest/assets_test.go index 90496b91..47f9ef05 100644 --- a/itest/assets_test.go +++ b/itest/assets_test.go @@ -1282,6 +1282,20 @@ func assertChannelAssetBalanceWithDelta(t *testing.T, node *HarnessNode, require.InDelta(t, remote, assetBalance.RemoteBalance, delta) } +func channelAssetBalance(t *testing.T, node *HarnessNode, + chanPoint *lnrpc.ChannelPoint) (uint64, uint64) { + + targetChan := fetchChannel(t, node, chanPoint) + + var assetBalance rfqmsg.JsonAssetChannel + err := json.Unmarshal(targetChan.CustomChannelData, &assetBalance) + require.NoError(t, err) + + require.GreaterOrEqual(t, len(assetBalance.FundingAssets), 1) + + return assetBalance.LocalBalance, assetBalance.RemoteBalance +} + // addRoutingFee adds the default routing fee (1 part per million fee rate plus // 1000 milli-satoshi base fee) to the given milli-satoshi amount. func addRoutingFee(amt lnwire.MilliSatoshi) lnwire.MilliSatoshi { @@ -1321,6 +1335,8 @@ func sendAssetKeySendPayment(t *testing.T, src, dst *HarnessNode, amt uint64, PaymentHash: hash[:], TimeoutSeconds: int32(PaymentTimeout.Seconds()), MaxParts: cfg.maxShards, + OutgoingChanIds: cfg.outgoingChanIDs, + AllowSelfPayment: cfg.allowSelfPayment, } request := &tchrpc.SendPaymentRequest{ @@ -1402,7 +1418,13 @@ func createAndPayNormalInvoiceWithBtc(t *testing.T, src, dst *HarnessNode, } func createNormalInvoice(t *testing.T, dst *HarnessNode, - amountSat btcutil.Amount) *lnrpc.AddInvoiceResponse { + amountSat btcutil.Amount, + opts ...invoiceOpt) *lnrpc.AddInvoiceResponse { + + cfg := defaultInvoiceConfig() + for _, opt := range opts { + opt(cfg) + } ctxb := context.Background() ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) @@ -1410,9 +1432,10 @@ func createNormalInvoice(t *testing.T, dst *HarnessNode, expirySeconds := 10 invoiceResp, err := dst.AddInvoice(ctxt, &lnrpc.Invoice{ - Value: int64(amountSat), - Memo: "normal invoice", - Expiry: int64(expirySeconds), + Value: int64(amountSat), + Memo: "normal invoice", + Expiry: int64(expirySeconds), + RouteHints: cfg.routeHints, }) require.NoError(t, err) @@ -1448,12 +1471,6 @@ func payPayReqWithSatoshi(t *testing.T, payer *HarnessNode, payReq string, ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) defer cancel() - shardSize := uint64(0) - - if cfg.smallShards { - shardSize = 80_000_000 - } - sendReq := &routerrpc.SendPaymentRequest{ PaymentRequest: payReq, TimeoutSeconds: int32(PaymentTimeout.Seconds()), @@ -1461,7 +1478,6 @@ func payPayReqWithSatoshi(t *testing.T, payer *HarnessNode, payReq string, MaxParts: cfg.maxShards, OutgoingChanIds: cfg.outgoingChanIDs, AllowSelfPayment: cfg.allowSelfPayment, - MaxShardSizeMsat: shardSize, } if cfg.smallShards { @@ -1652,6 +1668,8 @@ func payInvoiceWithAssets(t *testing.T, payer, rfqPeer *HarnessNode, FeeLimitMsat: int64(cfg.feeLimit), DestCustomRecords: cfg.destCustomRecords, MaxParts: cfg.maxShards, + OutgoingChanIds: cfg.outgoingChanIDs, + AllowSelfPayment: cfg.allowSelfPayment, } if cfg.smallShards { @@ -1768,6 +1786,12 @@ func withMsatAmount(amt uint64) invoiceOpt { } } +func withRouteHints(hints []*lnrpc.RouteHint) invoiceOpt { + return func(c *invoiceConfig) { + c.routeHints = hints + } +} + func createAssetInvoice(t *testing.T, dstRfqPeer, dst *HarnessNode, assetAmount uint64, assetID []byte, opts ...invoiceOpt) *lnrpc.AddInvoiceResponse { @@ -1795,14 +1819,14 @@ func createAssetInvoice(t *testing.T, dstRfqPeer, dst *HarnessNode, dstTapd := newTapClient(t, dst) request := &tchrpc.AddInvoiceRequest{ - GroupKey: cfg.groupKey, AssetAmount: assetAmount, PeerPubkey: peerPubKey, InvoiceRequest: &lnrpc.Invoice{ Memo: fmt.Sprintf("this is an asset invoice for "+ "%d units", assetAmount), - Expiry: timeoutSeconds, - ValueMsat: int64(cfg.msats), + Expiry: timeoutSeconds, + ValueMsat: int64(cfg.msats), + RouteHints: cfg.routeHints, }, } diff --git a/itest/litd_custom_channels_test.go b/itest/litd_custom_channels_test.go index 8e330267..20afa5ea 100644 --- a/itest/litd_custom_channels_test.go +++ b/itest/litd_custom_channels_test.go @@ -4611,6 +4611,8 @@ func testCustomChannelsSelfPayment(ctx context.Context, net *NetworkHarness, ) defer closeChannelAndAssert(t, net, alice, satChanPoint, false) + assetChan := fetchChannel(t.t, alice, assetChanPoint) + assetChanSCID := assetChan.ChanId satChan := fetchChannel(t.t, alice, satChanPoint) satChanSCID := satChan.ChanId @@ -4624,6 +4626,7 @@ func testCustomChannelsSelfPayment(ctx context.Context, net *NetworkHarness, assetKeySendAmount = 15_000 numInvoicePayments = 10 assetInvoiceAmount = 1_234 + btcInvoiceAmount = 10_000 btcKeySendAmount = 200_000 btcReserveAmount = 2000 btcHtlcCost = numInvoicePayments * 354 @@ -4695,4 +4698,75 @@ func testCustomChannelsSelfPayment(ctx context.Context, net *NetworkHarness, decodedInvoice.NumSatoshis, ) } + + // We now do the opposite: We create a satoshi invoice on Alice and + // attempt to pay it with assets. + aliceAssetBalance, bobAssetBalance = channelAssetBalance( + t.t, alice, assetChanPoint, + ) + for i := 0; i < numInvoicePayments; i++ { + // The BTC balance of Alice before we start the payment. We + // expect that to go down by at least the invoice amount. + btcBalanceAliceBefore := fetchChannel( + t.t, alice, satChanPoint, + ).LocalBalance + + hopHint := &lnrpc.HopHint{ + NodeId: bob.PubKeyStr, + ChanId: satChan.PeerScidAlias, + CltvExpiryDelta: 80, + FeeBaseMsat: 1000, + FeeProportionalMillionths: 1, + } + invoiceResp := createNormalInvoice( + t.t, alice, btcInvoiceAmount, withRouteHints( + []*lnrpc.RouteHint{{ + HopHints: []*lnrpc.HopHint{hopHint}, + }}, + ), + ) + sentUnits, _ := payInvoiceWithAssets( + t.t, alice, bob, invoiceResp.PaymentRequest, assetID, + withAllowSelfPayment(), withOutgoingChanIDs( + []uint64{assetChanSCID}, + ), + ) + + logBalance( + t.t, nodes, assetID, + "after paying sat invoice "+strconv.Itoa(i), + ) + + // The accumulated delta from the rounding of multiple sends. + // We basically allow the balance to be off by one unit for each + // payment. + delta := float64(i + 1) + + // We now expect the channel balance to have increased in the + // BTC channel and decreased in the assets channel. + assertChannelAssetBalanceWithDelta( + t.t, alice, assetChanPoint, + aliceAssetBalance-sentUnits, + bobAssetBalance+sentUnits, delta, + ) + aliceAssetBalance -= sentUnits + bobAssetBalance += sentUnits + + btcBalanceAliceAfter := fetchChannel( + t.t, alice, satChanPoint, + ).LocalBalance + + // The difference between the two balances should be at least + // the invoice amount. + decodedInvoice, err := alice.DecodePayReq( + context.Background(), &lnrpc.PayReqString{ + PayReq: invoiceResp.PaymentRequest, + }, + ) + require.NoError(t.t, err) + require.GreaterOrEqual( + t.t, btcBalanceAliceAfter-btcBalanceAliceBefore, + decodedInvoice.NumSatoshis, + ) + } }