Skip to content

Commit b41c464

Browse files
committed
itest: add bandwidth test
1 parent 3b66307 commit b41c464

File tree

2 files changed

+230
-5
lines changed

2 files changed

+230
-5
lines changed

itest/litd_custom_channels_test.go

Lines changed: 226 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import (
1818
"github.com/lightninglabs/taproot-assets/tapchannel"
1919
"github.com/lightninglabs/taproot-assets/taprpc"
2020
"github.com/lightninglabs/taproot-assets/taprpc/mintrpc"
21+
oraclerpc "github.com/lightninglabs/taproot-assets/taprpc/priceoraclerpc"
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"
@@ -1751,7 +1753,7 @@ func testCustomChannelsBreach(_ context.Context, net *NetworkHarness,
17511753

17521754
// testCustomChannelsLiquidityEdgeCases is a test that runs through some
17531755
// taproot asset channel liquidity related edge cases.
1754-
func testCustomChannelsLiquidityEdgeCases(_ context.Context,
1756+
func testCustomChannelsLiquidityEdgeCases(ctxb context.Context,
17551757
net *NetworkHarness, t *harnessTest) {
17561758

17571759
lndArgs := slices.Clone(lndArgsTemplate)
@@ -2039,11 +2041,8 @@ func testCustomChannelsLiquidityEdgeCases(_ context.Context,
20392041
// Yara with satoshi. For the last hop we try to settle the invoice in
20402042
// satoshi, where we will check whether Charlie's strict forwarding
20412043
// works as expected.
2042-
invoiceResp = createAssetInvoice(
2043-
t.t, charlie, dave, 1, assetID,
2044-
)
2044+
invoiceResp = createAssetInvoice(t.t, charlie, dave, 1, assetID)
20452045

2046-
ctxb := context.Background()
20472046
stream, err := dave.InvoicesClient.SubscribeSingleInvoice(
20482047
ctxb, &invoicesrpc.SubscribeSingleInvoiceRequest{
20492048
RHash: invoiceResp.RHash,
@@ -3319,3 +3318,225 @@ func runCustomChannelsHtlcForceClose(ctxb context.Context, t *harnessTest,
33193318
t.t, zaneTap, assetID, zaneExpectedBalance,
33203319
)
33213320
}
3321+
3322+
// testCustomChannelsForwardBandwidth is a test that runs through some Taproot
3323+
// Assets Channel liquidity edge cases, specifically related to forwarding HTLCs
3324+
// into channels with no available asset bandwidth.
3325+
func testCustomChannelsForwardBandwidth(ctxb context.Context,
3326+
net *NetworkHarness, t *harnessTest) {
3327+
3328+
lndArgs := slices.Clone(lndArgsTemplate)
3329+
litdArgs := slices.Clone(litdArgsTemplate)
3330+
3331+
// Explicitly set the proof courier as Zane (now has no other role
3332+
// other than proof shuffling), otherwise a hashmail courier will be
3333+
// used. For the funding transaction, we're just posting it and don't
3334+
// expect a true receiver.
3335+
zane, err := net.NewNode(
3336+
t.t, "Zane", lndArgs, false, true, litdArgs...,
3337+
)
3338+
require.NoError(t.t, err)
3339+
3340+
litdArgs = append(litdArgs, fmt.Sprintf(
3341+
"--taproot-assets.proofcourieraddr=%s://%s",
3342+
proof.UniverseRpcCourierType, zane.Cfg.LitAddr(),
3343+
))
3344+
3345+
// The topology we are going for looks like the following:
3346+
//
3347+
// Charlie --[assets]--> Dave --[sats]--> Erin --[assets]--> Fabia
3348+
// |
3349+
// |
3350+
// [assets]
3351+
// |
3352+
// v
3353+
// Yara
3354+
//
3355+
// With [assets] being a custom channel and [sats] being a normal, BTC
3356+
// only channel.
3357+
// All 5 nodes need to be full litd nodes running in integrated mode
3358+
// with tapd included. We also need specific flags to be enabled, so we
3359+
// create 5 completely new nodes, ignoring the two default nodes that
3360+
// are created by the harness.
3361+
charlie, err := net.NewNode(
3362+
t.t, "Charlie", lndArgs, false, true, litdArgs...,
3363+
)
3364+
require.NoError(t.t, err)
3365+
3366+
dave, err := net.NewNode(t.t, "Dave", lndArgs, false, true, litdArgs...)
3367+
require.NoError(t.t, err)
3368+
erin, err := net.NewNode(t.t, "Erin", lndArgs, false, true, litdArgs...)
3369+
require.NoError(t.t, err)
3370+
fabia, err := net.NewNode(
3371+
t.t, "Fabia", lndArgs, false, true, litdArgs...,
3372+
)
3373+
require.NoError(t.t, err)
3374+
yara, err := net.NewNode(
3375+
t.t, "Yara", lndArgs, false, true, litdArgs...,
3376+
)
3377+
require.NoError(t.t, err)
3378+
3379+
nodes := []*HarnessNode{charlie, dave, erin, fabia, yara}
3380+
connectAllNodes(t.t, net, nodes)
3381+
fundAllNodes(t.t, net, nodes)
3382+
3383+
// Create the normal channel between Dave and Erin.
3384+
t.Logf("Opening normal channel between Dave and Erin...")
3385+
channelOp := openChannelAndAssert(
3386+
t, net, dave, erin, lntest.OpenChannelParams{
3387+
Amt: 10_000_000,
3388+
SatPerVByte: 5,
3389+
},
3390+
)
3391+
defer closeChannelAndAssert(t, net, dave, channelOp, false)
3392+
3393+
// This is the only public channel, we need everyone to be aware of it.
3394+
assertChannelKnown(t.t, charlie, channelOp)
3395+
assertChannelKnown(t.t, fabia, channelOp)
3396+
3397+
universeTap := newTapClient(t.t, zane)
3398+
charlieTap := newTapClient(t.t, charlie)
3399+
daveTap := newTapClient(t.t, dave)
3400+
erinTap := newTapClient(t.t, erin)
3401+
fabiaTap := newTapClient(t.t, fabia)
3402+
yaraTap := newTapClient(t.t, yara)
3403+
3404+
// Mint an asset on Charlie and sync all nodes to Charlie as the
3405+
// universe.
3406+
mintedAssets := itest.MintAssetsConfirmBatch(
3407+
t.t, t.lndHarness.Miner.Client, charlieTap,
3408+
[]*mintrpc.MintAssetRequest{
3409+
{
3410+
Asset: itestAsset,
3411+
},
3412+
},
3413+
)
3414+
cents := mintedAssets[0]
3415+
assetID := cents.AssetGenesis.AssetId
3416+
3417+
t.Logf("Minted %d lightning cents, syncing universes...", cents.Amount)
3418+
syncUniverses(t.t, charlieTap, dave, erin, fabia, yara)
3419+
t.Logf("Universes synced between all nodes, distributing assets...")
3420+
3421+
const (
3422+
daveFundingAmount = uint64(400_000)
3423+
erinFundingAmount = uint64(200_000)
3424+
)
3425+
charlieFundingAmount := cents.Amount - uint64(2*400_000)
3426+
3427+
_, _, chanPointEF := createTestAssetNetwork(
3428+
t, net, charlieTap, daveTap, erinTap, fabiaTap, yaraTap,
3429+
universeTap, cents, 400_000, charlieFundingAmount,
3430+
daveFundingAmount, erinFundingAmount, 0,
3431+
)
3432+
3433+
// Before we start sending out payments, let's make sure each node can
3434+
// see the other one in the graph and has all required features.
3435+
require.NoError(t.t, t.lndHarness.AssertNodeKnown(charlie, dave))
3436+
require.NoError(t.t, t.lndHarness.AssertNodeKnown(dave, charlie))
3437+
require.NoError(t.t, t.lndHarness.AssertNodeKnown(dave, yara))
3438+
require.NoError(t.t, t.lndHarness.AssertNodeKnown(yara, dave))
3439+
require.NoError(t.t, t.lndHarness.AssertNodeKnown(erin, fabia))
3440+
require.NoError(t.t, t.lndHarness.AssertNodeKnown(fabia, erin))
3441+
require.NoError(t.t, t.lndHarness.AssertNodeKnown(charlie, erin))
3442+
3443+
logBalance(t.t, nodes, assetID, "initial")
3444+
3445+
// We now deplete the channel between Erin and Fabia by moving all
3446+
// assets to Fabia.
3447+
sendAssetKeySendPayment(
3448+
t.t, erin, fabia, erinFundingAmount, assetID,
3449+
fn.None[int64](), lnrpc.Payment_SUCCEEDED,
3450+
fn.None[lnrpc.PaymentFailureReason](),
3451+
)
3452+
logBalance(t.t, nodes, assetID, "after moving assets to Fabia")
3453+
3454+
// Test case 1: We cannot keysend more assets from Erin to Fabia.
3455+
sendAssetKeySendPayment(
3456+
t.t, erin, fabia, 1, assetID, fn.None[int64](),
3457+
lnrpc.Payment_FAILED, fn.Some(errNoBalance),
3458+
)
3459+
3460+
// Test case 2: We cannot pay an invoice from Charlie to Fabia.
3461+
invoiceResp := createAssetInvoice(t.t, erin, fabia, 123, assetID)
3462+
payInvoiceWithSatoshi(
3463+
t.t, charlie, invoiceResp, lnrpc.Payment_FAILED,
3464+
fn.Some(errNoRoute),
3465+
)
3466+
3467+
// Test case 3: We now create an asset buy order for a normal amount of
3468+
// assets. We then "fake" an invoice referencing that buy order that
3469+
// is for an amount that is too small to be paid with a single asset
3470+
// unit. This should be handled gracefully and not lead to a crash.
3471+
// Ideally such an invoice shouldn't be created in the first place, but
3472+
// we want to make sure that the system doesn't crash in this case.
3473+
numUnits := uint64(10)
3474+
buyOrderResp, err := fabiaTap.RfqClient.AddAssetBuyOrder(
3475+
ctxb, &rfqrpc.AddAssetBuyOrderRequest{
3476+
AssetSpecifier: &rfqrpc.AssetSpecifier{
3477+
Id: &rfqrpc.AssetSpecifier_AssetId{
3478+
AssetId: assetID,
3479+
},
3480+
},
3481+
AssetMaxAmt: numUnits,
3482+
Expiry: uint64(
3483+
time.Now().Add(time.Hour).Unix(),
3484+
),
3485+
PeerPubKey: erin.PubKey[:],
3486+
TimeoutSeconds: 10,
3487+
},
3488+
)
3489+
require.NoError(t.t, err)
3490+
3491+
quoteResp := buyOrderResp.Response
3492+
quote, ok := quoteResp.(*rfqrpc.AddAssetBuyOrderResponse_AcceptedQuote)
3493+
require.True(t.t, ok)
3494+
3495+
// We calculate the milli-satoshi amount one below the equivalent of a
3496+
// single asset unit.
3497+
rate, err := oraclerpc.UnmarshalFixedPoint(&oraclerpc.FixedPoint{
3498+
Coefficient: quote.AcceptedQuote.AskAssetRate.Coefficient,
3499+
Scale: quote.AcceptedQuote.AskAssetRate.Scale,
3500+
})
3501+
require.NoError(t.t, err)
3502+
3503+
oneUnit := rfqmath.NewBigIntFixedPoint(1, 0)
3504+
oneUnitMilliSat := rfqmath.UnitsToMilliSatoshi(oneUnit, *rate)
3505+
3506+
t.Logf("Got quote for %v asset units per BTC", rate)
3507+
msatPerUnit := float64(oneUnitMilliSat) / float64(1)
3508+
t.Logf("Got quote for %v asset units at %3f msat/unit from peer %s "+
3509+
"with SCID %d", numUnits, msatPerUnit, erin.PubKeyStr,
3510+
quote.AcceptedQuote.Scid)
3511+
3512+
// We now manually add the invoice in order to inject the above,
3513+
// manually generated, quote.
3514+
invoiceResp2, err := fabia.AddInvoice(ctxb, &lnrpc.Invoice{
3515+
Memo: "too small invoice",
3516+
ValueMsat: int64(oneUnitMilliSat - 1),
3517+
RouteHints: []*lnrpc.RouteHint{{
3518+
HopHints: []*lnrpc.HopHint{{
3519+
NodeId: erin.PubKeyStr,
3520+
ChanId: quote.AcceptedQuote.Scid,
3521+
}},
3522+
}},
3523+
})
3524+
require.NoError(t.t, err)
3525+
3526+
payInvoiceWithSatoshi(
3527+
t.t, dave, invoiceResp2, lnrpc.Payment_FAILED,
3528+
fn.Some(errNoRoute),
3529+
)
3530+
3531+
// Let's make sure we can still use the channel between Erin and Fabia
3532+
// by doing a satoshi keysend payment.
3533+
sendKeySendPayment(t.t, erin, fabia, 2000)
3534+
logBalance(t.t, nodes, assetID, "after BTC only keysend")
3535+
3536+
// Finally, we close the channel between Erin and Fabia to make sure
3537+
// everything is settled correctly.
3538+
closeAssetChannelAndAssert(
3539+
t, net, erin, fabia, chanPointEF, assetID, nil,
3540+
universeTap, noOpCoOpCloseBalanceCheck,
3541+
)
3542+
}

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)