Skip to content

Commit 492d1e6

Browse files
committed
itest: add bandwidth test
1 parent 6ed024c commit 492d1e6

File tree

2 files changed

+229
-5
lines changed

2 files changed

+229
-5
lines changed

itest/litd_custom_channels_test.go

Lines changed: 225 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/lightninglabs/taproot-assets/tapchannel"
2121
"github.com/lightninglabs/taproot-assets/taprpc"
2222
"github.com/lightninglabs/taproot-assets/taprpc/mintrpc"
23+
oraclerpc "github.com/lightninglabs/taproot-assets/taprpc/priceoraclerpc"
2324
"github.com/lightninglabs/taproot-assets/taprpc/rfqrpc"
2425
tchrpc "github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc"
2526
"github.com/lightninglabs/taproot-assets/taprpc/universerpc"
@@ -1744,7 +1745,7 @@ func testCustomChannelsBreach(_ context.Context, net *NetworkHarness,
17441745

17451746
// testCustomChannelsLiquidityEdgeCases is a test that runs through some
17461747
// taproot asset channel liquidity related edge cases.
1747-
func testCustomChannelsLiquidityEdgeCases(_ context.Context,
1748+
func testCustomChannelsLiquidityEdgeCases(ctxb context.Context,
17481749
net *NetworkHarness, t *harnessTest) {
17491750

17501751
lndArgs := slices.Clone(lndArgsTemplate)
@@ -2032,11 +2033,8 @@ func testCustomChannelsLiquidityEdgeCases(_ context.Context,
20322033
// satoshi, where we will check whether Dave's strict forwarding works
20332034
// as expected. Charlie is only used as a dummy RFQ peer in this case,
20342035
// Yara totally ignored the RFQ hint and pays agnostically with sats.
2035-
invoiceResp = createAssetInvoice(
2036-
t.t, charlie, dave, 1, assetID,
2037-
)
2036+
invoiceResp = createAssetInvoice(t.t, charlie, dave, 1, assetID)
20382037

2039-
ctxb := context.Background()
20402038
stream, err := dave.InvoicesClient.SubscribeSingleInvoice(
20412039
ctxb, &invoicesrpc.SubscribeSingleInvoiceRequest{
20422040
RHash: invoiceResp.RHash,
@@ -3461,3 +3459,225 @@ func runCustomChannelsHtlcForceClose(ctxb context.Context, t *harnessTest,
34613459
t.t, zaneTap, assetID, zaneExpectedBalance,
34623460
)
34633461
}
3462+
3463+
// testCustomChannelsForwardBandwidth is a test that runs through some Taproot
3464+
// Assets Channel liquidity edge cases, specifically related to forwarding HTLCs
3465+
// into channels with no available asset bandwidth.
3466+
func testCustomChannelsForwardBandwidth(ctxb context.Context,
3467+
net *NetworkHarness, t *harnessTest) {
3468+
3469+
lndArgs := slices.Clone(lndArgsTemplate)
3470+
litdArgs := slices.Clone(litdArgsTemplate)
3471+
3472+
// Explicitly set the proof courier as Zane (now has no other role
3473+
// other than proof shuffling), otherwise a hashmail courier will be
3474+
// used. For the funding transaction, we're just posting it and don't
3475+
// expect a true receiver.
3476+
zane, err := net.NewNode(
3477+
t.t, "Zane", lndArgs, false, true, litdArgs...,
3478+
)
3479+
require.NoError(t.t, err)
3480+
3481+
litdArgs = append(litdArgs, fmt.Sprintf(
3482+
"--taproot-assets.proofcourieraddr=%s://%s",
3483+
proof.UniverseRpcCourierType, zane.Cfg.LitAddr(),
3484+
))
3485+
3486+
// The topology we are going for looks like the following:
3487+
//
3488+
// Charlie --[assets]--> Dave --[sats]--> Erin --[assets]--> Fabia
3489+
// |
3490+
// |
3491+
// [assets]
3492+
// |
3493+
// v
3494+
// Yara
3495+
//
3496+
// With [assets] being a custom channel and [sats] being a normal, BTC
3497+
// only channel.
3498+
// All 5 nodes need to be full litd nodes running in integrated mode
3499+
// with tapd included. We also need specific flags to be enabled, so we
3500+
// create 5 completely new nodes, ignoring the two default nodes that
3501+
// are created by the harness.
3502+
charlie, err := net.NewNode(
3503+
t.t, "Charlie", lndArgs, false, true, litdArgs...,
3504+
)
3505+
require.NoError(t.t, err)
3506+
3507+
dave, err := net.NewNode(t.t, "Dave", lndArgs, false, true, litdArgs...)
3508+
require.NoError(t.t, err)
3509+
erin, err := net.NewNode(t.t, "Erin", lndArgs, false, true, litdArgs...)
3510+
require.NoError(t.t, err)
3511+
fabia, err := net.NewNode(
3512+
t.t, "Fabia", lndArgs, false, true, litdArgs...,
3513+
)
3514+
require.NoError(t.t, err)
3515+
yara, err := net.NewNode(
3516+
t.t, "Yara", lndArgs, false, true, litdArgs...,
3517+
)
3518+
require.NoError(t.t, err)
3519+
3520+
nodes := []*HarnessNode{charlie, dave, erin, fabia, yara}
3521+
connectAllNodes(t.t, net, nodes)
3522+
fundAllNodes(t.t, net, nodes)
3523+
3524+
// Create the normal channel between Dave and Erin.
3525+
t.Logf("Opening normal channel between Dave and Erin...")
3526+
channelOp := openChannelAndAssert(
3527+
t, net, dave, erin, lntest.OpenChannelParams{
3528+
Amt: 10_000_000,
3529+
SatPerVByte: 5,
3530+
},
3531+
)
3532+
defer closeChannelAndAssert(t, net, dave, channelOp, false)
3533+
3534+
// This is the only public channel, we need everyone to be aware of it.
3535+
assertChannelKnown(t.t, charlie, channelOp)
3536+
assertChannelKnown(t.t, fabia, channelOp)
3537+
3538+
universeTap := newTapClient(t.t, zane)
3539+
charlieTap := newTapClient(t.t, charlie)
3540+
daveTap := newTapClient(t.t, dave)
3541+
erinTap := newTapClient(t.t, erin)
3542+
fabiaTap := newTapClient(t.t, fabia)
3543+
yaraTap := newTapClient(t.t, yara)
3544+
3545+
// Mint an asset on Charlie and sync all nodes to Charlie as the
3546+
// universe.
3547+
mintedAssets := itest.MintAssetsConfirmBatch(
3548+
t.t, t.lndHarness.Miner.Client, charlieTap,
3549+
[]*mintrpc.MintAssetRequest{
3550+
{
3551+
Asset: itestAsset,
3552+
},
3553+
},
3554+
)
3555+
cents := mintedAssets[0]
3556+
assetID := cents.AssetGenesis.AssetId
3557+
3558+
t.Logf("Minted %d lightning cents, syncing universes...", cents.Amount)
3559+
syncUniverses(t.t, charlieTap, dave, erin, fabia, yara)
3560+
t.Logf("Universes synced between all nodes, distributing assets...")
3561+
3562+
const (
3563+
daveFundingAmount = uint64(400_000)
3564+
erinFundingAmount = uint64(200_000)
3565+
)
3566+
charlieFundingAmount := cents.Amount - uint64(2*400_000)
3567+
3568+
_, _, chanPointEF := createTestAssetNetwork(
3569+
t, net, charlieTap, daveTap, erinTap, fabiaTap, yaraTap,
3570+
universeTap, cents, 400_000, charlieFundingAmount,
3571+
daveFundingAmount, erinFundingAmount, 0,
3572+
)
3573+
3574+
// Before we start sending out payments, let's make sure each node can
3575+
// see the other one in the graph and has all required features.
3576+
require.NoError(t.t, t.lndHarness.AssertNodeKnown(charlie, dave))
3577+
require.NoError(t.t, t.lndHarness.AssertNodeKnown(dave, charlie))
3578+
require.NoError(t.t, t.lndHarness.AssertNodeKnown(dave, yara))
3579+
require.NoError(t.t, t.lndHarness.AssertNodeKnown(yara, dave))
3580+
require.NoError(t.t, t.lndHarness.AssertNodeKnown(erin, fabia))
3581+
require.NoError(t.t, t.lndHarness.AssertNodeKnown(fabia, erin))
3582+
require.NoError(t.t, t.lndHarness.AssertNodeKnown(charlie, erin))
3583+
3584+
logBalance(t.t, nodes, assetID, "initial")
3585+
3586+
// We now deplete the channel between Erin and Fabia by moving all
3587+
// assets to Fabia.
3588+
sendAssetKeySendPayment(
3589+
t.t, erin, fabia, erinFundingAmount, assetID,
3590+
fn.None[int64](), lnrpc.Payment_SUCCEEDED,
3591+
fn.None[lnrpc.PaymentFailureReason](),
3592+
)
3593+
logBalance(t.t, nodes, assetID, "after moving assets to Fabia")
3594+
3595+
// Test case 1: We cannot keysend more assets from Erin to Fabia.
3596+
sendAssetKeySendPayment(
3597+
t.t, erin, fabia, 1, assetID, fn.None[int64](),
3598+
lnrpc.Payment_FAILED, fn.Some(errNoBalance),
3599+
)
3600+
3601+
// Test case 2: We cannot pay an invoice from Charlie to Fabia.
3602+
invoiceResp := createAssetInvoice(t.t, erin, fabia, 123, assetID)
3603+
payInvoiceWithSatoshi(
3604+
t.t, charlie, invoiceResp, lnrpc.Payment_FAILED,
3605+
fn.Some(errNoRoute),
3606+
)
3607+
3608+
// Test case 3: We now create an asset buy order for a normal amount of
3609+
// assets. We then "fake" an invoice referencing that buy order that
3610+
// is for an amount that is too small to be paid with a single asset
3611+
// unit. This should be handled gracefully and not lead to a crash.
3612+
// Ideally such an invoice shouldn't be created in the first place, but
3613+
// we want to make sure that the system doesn't crash in this case.
3614+
numUnits := uint64(10)
3615+
buyOrderResp, err := fabiaTap.RfqClient.AddAssetBuyOrder(
3616+
ctxb, &rfqrpc.AddAssetBuyOrderRequest{
3617+
AssetSpecifier: &rfqrpc.AssetSpecifier{
3618+
Id: &rfqrpc.AssetSpecifier_AssetId{
3619+
AssetId: assetID,
3620+
},
3621+
},
3622+
AssetMaxAmt: numUnits,
3623+
Expiry: uint64(
3624+
time.Now().Add(time.Hour).Unix(),
3625+
),
3626+
PeerPubKey: erin.PubKey[:],
3627+
TimeoutSeconds: 10,
3628+
},
3629+
)
3630+
require.NoError(t.t, err)
3631+
3632+
quoteResp := buyOrderResp.Response
3633+
quote, ok := quoteResp.(*rfqrpc.AddAssetBuyOrderResponse_AcceptedQuote)
3634+
require.True(t.t, ok)
3635+
3636+
// We calculate the milli-satoshi amount one below the equivalent of a
3637+
// single asset unit.
3638+
rate, err := oraclerpc.UnmarshalFixedPoint(&oraclerpc.FixedPoint{
3639+
Coefficient: quote.AcceptedQuote.AskAssetRate.Coefficient,
3640+
Scale: quote.AcceptedQuote.AskAssetRate.Scale,
3641+
})
3642+
require.NoError(t.t, err)
3643+
3644+
oneUnit := rfqmath.NewBigIntFixedPoint(1, 0)
3645+
oneUnitMilliSat := rfqmath.UnitsToMilliSatoshi(oneUnit, *rate)
3646+
3647+
t.Logf("Got quote for %v asset units per BTC", rate)
3648+
msatPerUnit := float64(oneUnitMilliSat) / float64(1)
3649+
t.Logf("Got quote for %v asset units at %3f msat/unit from peer %s "+
3650+
"with SCID %d", numUnits, msatPerUnit, erin.PubKeyStr,
3651+
quote.AcceptedQuote.Scid)
3652+
3653+
// We now manually add the invoice in order to inject the above,
3654+
// manually generated, quote.
3655+
invoiceResp2, err := fabia.AddInvoice(ctxb, &lnrpc.Invoice{
3656+
Memo: "too small invoice",
3657+
ValueMsat: int64(oneUnitMilliSat - 1),
3658+
RouteHints: []*lnrpc.RouteHint{{
3659+
HopHints: []*lnrpc.HopHint{{
3660+
NodeId: erin.PubKeyStr,
3661+
ChanId: quote.AcceptedQuote.Scid,
3662+
}},
3663+
}},
3664+
})
3665+
require.NoError(t.t, err)
3666+
3667+
payInvoiceWithSatoshi(
3668+
t.t, dave, invoiceResp2, lnrpc.Payment_FAILED,
3669+
fn.Some(errNoRoute),
3670+
)
3671+
3672+
// Let's make sure we can still use the channel between Erin and Fabia
3673+
// by doing a satoshi keysend payment.
3674+
sendKeySendPayment(t.t, erin, fabia, 2000)
3675+
logBalance(t.t, nodes, assetID, "after BTC only keysend")
3676+
3677+
// Finally, we close the channel between Erin and Fabia to make sure
3678+
// everything is settled correctly.
3679+
closeAssetChannelAndAssert(
3680+
t, net, erin, fabia, chanPointEF, assetID, nil,
3681+
universeTap, noOpCoOpCloseBalanceCheck,
3682+
)
3683+
}

itest/litd_test_list_on_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,8 @@ var allTestCases = []*testCase{
6868
name: "test custom channels fee",
6969
test: testCustomChannelsFee,
7070
},
71+
{
72+
name: "test custom channels forward bandwidth",
73+
test: testCustomChannelsForwardBandwidth,
74+
},
7175
}

0 commit comments

Comments
 (0)