Skip to content

Commit d85b327

Browse files
committed
itest: add custom channels liquidity edge cases test
1 parent bf60d5b commit d85b327

File tree

3 files changed

+225
-19
lines changed

3 files changed

+225
-19
lines changed

itest/assets_test.go

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"fmt"
1010
"os"
1111
"testing"
12+
"time"
1213

1314
"github.com/btcsuite/btcd/btcec/v2"
1415
"github.com/btcsuite/btcd/btcec/v2/schnorr"
@@ -45,6 +46,10 @@ import (
4546
"gopkg.in/macaroon.v2"
4647
)
4748

49+
var (
50+
PaymentTimeoutSec = int32(6)
51+
)
52+
4853
// createTestAssetNetwork sends asset funds from Charlie to Dave and Erin, so
4954
// they can fund asset channels with Yara and Fabia, respectively. So the asset
5055
// channels created are Charlie->Dave, Dave->Yara, Erin->Fabia. The channels
@@ -429,9 +434,10 @@ func assertPendingChannels(t *testing.T, node *HarnessNode, assetID []byte,
429434

430435
require.NotZero(t, pendingJSON.Assets[0].Capacity)
431436

432-
pendingLocalBalance, pendingRemoteBalance := getAssetChannelBalance(
433-
t, node, assetID, true,
434-
)
437+
pendingLocalBalance, pendingRemoteBalance, _, _ :=
438+
getAssetChannelBalance(
439+
t, node, assetID, true,
440+
)
435441
require.EqualValues(t, localSum, pendingLocalBalance)
436442
require.EqualValues(t, remoteSum, pendingRemoteBalance)
437443
}
@@ -542,7 +548,7 @@ func getChannelCustomData(src, dst *HarnessNode) (*rfqmsg.JsonAssetChanInfo,
542548
}
543549

544550
func getAssetChannelBalance(t *testing.T, node *HarnessNode, assetID []byte,
545-
pending bool) (uint64, uint64) {
551+
pending bool) (uint64, uint64, uint64, uint64) {
546552

547553
ctxb := context.Background()
548554
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
@@ -572,11 +578,13 @@ func getAssetChannelBalance(t *testing.T, node *HarnessNode, assetID []byte,
572578
remoteSum += balances[assetIDString].RemoteBalance
573579
}
574580

575-
return localSum, remoteSum
581+
return localSum, remoteSum, balance.LocalBalance.Sat,
582+
balance.RemoteBalance.Sat
576583
}
577584

578585
func sendAssetKeySendPayment(t *testing.T, src, dst *HarnessNode, amt uint64,
579-
assetID []byte, btcAmt fn.Option[int64]) {
586+
assetID []byte, btcAmt fn.Option[int64],
587+
expectedStatus lnrpc.Payment_PaymentStatus) {
580588

581589
ctxb := context.Background()
582590
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
@@ -601,7 +609,7 @@ func sendAssetKeySendPayment(t *testing.T, src, dst *HarnessNode, amt uint64,
601609
Amt: btcAmt.UnwrapOr(500),
602610
DestCustomRecords: customRecords,
603611
PaymentHash: hash[:],
604-
TimeoutSeconds: 3,
612+
TimeoutSeconds: PaymentTimeoutSec,
605613
}
606614

607615
stream, err := srcTapd.SendPayment(ctxt, &tchrpc.SendPaymentRequest{
@@ -613,7 +621,7 @@ func sendAssetKeySendPayment(t *testing.T, src, dst *HarnessNode, amt uint64,
613621

614622
result, err := getAssetPaymentResult(stream)
615623
require.NoError(t, err)
616-
require.Equal(t, lnrpc.Payment_SUCCEEDED, result.Status)
624+
require.Equal(t, expectedStatus, result.Status)
617625
}
618626

619627
func sendKeySendPayment(t *testing.T, src, dst *HarnessNode,
@@ -640,7 +648,7 @@ func sendKeySendPayment(t *testing.T, src, dst *HarnessNode,
640648
Amt: int64(amt),
641649
DestCustomRecords: customRecords,
642650
PaymentHash: hash[:],
643-
TimeoutSeconds: 3,
651+
TimeoutSeconds: PaymentTimeoutSec,
644652
}
645653

646654
stream, err := src.RouterClient.SendPaymentV2(ctxt, req)
@@ -698,7 +706,7 @@ func payInvoiceWithSatoshi(t *testing.T, payer *HarnessNode,
698706

699707
sendReq := &routerrpc.SendPaymentRequest{
700708
PaymentRequest: invoice.PaymentRequest,
701-
TimeoutSeconds: 2,
709+
TimeoutSeconds: PaymentTimeoutSec,
702710
MaxShardSizeMsat: 80_000_000,
703711
FeeLimitMsat: 1_000_000,
704712
}
@@ -727,7 +735,7 @@ func payInvoiceWithAssets(t *testing.T, payer, rfqPeer *HarnessNode,
727735

728736
sendReq := &routerrpc.SendPaymentRequest{
729737
PaymentRequest: invoice.PaymentRequest,
730-
TimeoutSeconds: 2,
738+
TimeoutSeconds: PaymentTimeoutSec,
731739
FeeLimitMsat: 1_000_000,
732740
}
733741

@@ -1422,10 +1430,15 @@ func logBalance(t *testing.T, nodes []*HarnessNode, assetID []byte,
14221430

14231431
t.Helper()
14241432

1433+
time.Sleep(time.Millisecond * 250)
1434+
14251435
for _, node := range nodes {
1426-
local, remote := getAssetChannelBalance(t, node, assetID, false)
1427-
t.Logf("%-7s balance: local=%-9d remote=%-9d (%v)",
1428-
node.Cfg.Name, local, remote, occasion)
1436+
local, remote, localSat, remoteSat :=
1437+
getAssetChannelBalance(t, node, assetID, false)
1438+
1439+
t.Logf("%-7s balance: local=%-9d remote=%-9d, localSat=%-9d, "+
1440+
"remoteSat=%-9d (%v)", node.Cfg.Name, local, remote,
1441+
localSat, remoteSat, occasion)
14291442
}
14301443
}
14311444

itest/litd_custom_channels_test.go

Lines changed: 194 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ func testCustomChannelsLarge(_ context.Context, net *NetworkHarness,
238238
fabiaInvoiceAssetAmount/2
239239
sendAssetKeySendPayment(
240240
t.t, charlie, dave, charlieRemainingBalance,
241-
assetID, fn.None[int64](),
241+
assetID, fn.None[int64](), lnrpc.Payment_SUCCEEDED,
242242
)
243243
logBalance(t.t, nodes, assetID, "after keysend")
244244

@@ -400,7 +400,7 @@ func testCustomChannels(_ context.Context, net *NetworkHarness,
400400
keySendAmount := charlieFundingAmount
401401
sendAssetKeySendPayment(
402402
t.t, charlie, dave, charlieFundingAmount, assetID,
403-
fn.None[int64](),
403+
fn.None[int64](), lnrpc.Payment_SUCCEEDED,
404404
)
405405
logBalance(t.t, nodes, assetID, "after keysend")
406406

@@ -431,7 +431,7 @@ func testCustomChannels(_ context.Context, net *NetworkHarness,
431431
// Let's keysend the rest of the balance back to Charlie.
432432
sendAssetKeySendPayment(
433433
t.t, dave, charlie, charlieFundingAmount-charlieInvoiceAmount,
434-
assetID, fn.None[int64](),
434+
assetID, fn.None[int64](), lnrpc.Payment_SUCCEEDED,
435435
)
436436
logBalance(t.t, nodes, assetID, "after keysend back")
437437

@@ -856,6 +856,7 @@ func testCustomChannelsGroupedAsset(_ context.Context, net *NetworkHarness,
856856
const keySendAmount = 100
857857
sendAssetKeySendPayment(
858858
t.t, charlie, dave, keySendAmount, assetID, fn.None[int64](),
859+
lnrpc.Payment_SUCCEEDED,
859860
)
860861
logBalance(t.t, nodes, assetID, "after keysend")
861862

@@ -867,6 +868,7 @@ func testCustomChannelsGroupedAsset(_ context.Context, net *NetworkHarness,
867868
// an HTLC.
868869
sendAssetKeySendPayment(
869870
t.t, dave, charlie, keySendAmount, assetID, fn.None[int64](),
871+
lnrpc.Payment_SUCCEEDED,
870872
)
871873
logBalance(t.t, nodes, assetID, "after keysend back")
872874

@@ -1266,7 +1268,7 @@ func testCustomChannelsForceClose(_ context.Context, net *NetworkHarness,
12661268
for i := 0; i < numPayments; i++ {
12671269
sendAssetKeySendPayment(
12681270
t.t, charlie, dave, keySendAmount, assetID,
1269-
fn.Some(btcAmt),
1271+
fn.Some(btcAmt), lnrpc.Payment_SUCCEEDED,
12701272
)
12711273
}
12721274

@@ -1590,7 +1592,7 @@ func testCustomChannelsBreach(_ context.Context, net *NetworkHarness,
15901592
for i := 0; i < numPayments; i++ {
15911593
sendAssetKeySendPayment(
15921594
t.t, charlie, dave, keySendAmount, assetID,
1593-
fn.Some(btcAmt),
1595+
fn.Some(btcAmt), lnrpc.Payment_SUCCEEDED,
15941596
)
15951597
}
15961598

@@ -1605,6 +1607,7 @@ func testCustomChannelsBreach(_ context.Context, net *NetworkHarness,
16051607
// just at above.
16061608
sendAssetKeySendPayment(
16071609
t.t, charlie, dave, keySendAmount, assetID, fn.Some(btcAmt),
1610+
lnrpc.Payment_SUCCEEDED,
16081611
)
16091612
logBalance(t.t, nodes, assetID, "after keysend -- final state")
16101613

@@ -1675,3 +1678,189 @@ func testCustomChannelsBreach(_ context.Context, net *NetworkHarness,
16751678

16761679
t.Logf("Charlie UTXOs after breach: %v", toProtoJSON(t.t, charlieUTXOs))
16771680
}
1681+
1682+
func testCustomChannelsLiquidityEdgeCases(_ context.Context,
1683+
net *NetworkHarness, t *harnessTest) {
1684+
1685+
ctxb := context.Background()
1686+
lndArgs := slices.Clone(lndArgsTemplate)
1687+
litdArgs := slices.Clone(litdArgsTemplate)
1688+
1689+
zane, err := net.NewNode(
1690+
t.t, "Zane", lndArgs, false, true, litdArgs...,
1691+
)
1692+
require.NoError(t.t, err)
1693+
1694+
litdArgs = append(litdArgs, fmt.Sprintf(
1695+
"--taproot-assets.proofcourieraddr=%s://%s",
1696+
proof.UniverseRpcCourierType, zane.Cfg.LitAddr(),
1697+
))
1698+
1699+
charlie, err := net.NewNode(
1700+
t.t, "Charlie", lndArgs, false, true, litdArgs...,
1701+
)
1702+
require.NoError(t.t, err)
1703+
dave, err := net.NewNode(t.t, "Dave", lndArgs, false, true, litdArgs...)
1704+
require.NoError(t.t, err)
1705+
erin, err := net.NewNode(t.t, "Erin", lndArgs, false, true, litdArgs...)
1706+
require.NoError(t.t, err)
1707+
1708+
nodes := []*HarnessNode{charlie, dave, erin}
1709+
connectAllNodes(t.t, net, nodes)
1710+
fundAllNodes(t.t, net, nodes)
1711+
1712+
// Create the normal channel between Dave and Erin.
1713+
t.Logf("Opening normal channel between Dave and Erin...")
1714+
channelOp := openChannelAndAssert(
1715+
t, net, dave, erin, lntest.OpenChannelParams{
1716+
Amt: 5_000_000,
1717+
SatPerVByte: 5,
1718+
},
1719+
)
1720+
defer closeChannelAndAssert(t, net, dave, channelOp, false)
1721+
1722+
assertChannelKnown(t.t, charlie, channelOp)
1723+
1724+
charlieTap := newTapClient(t.t, charlie)
1725+
daveTap := newTapClient(t.t, dave)
1726+
universeTap := newTapClient(t.t, zane)
1727+
1728+
// Mint an asset on Charlie and sync Dave to Charlie as the universe.
1729+
mintedAssets := itest.MintAssetsConfirmBatch(
1730+
t.t, t.lndHarness.Miner.Client, charlieTap,
1731+
[]*mintrpc.MintAssetRequest{
1732+
{
1733+
Asset: itestAsset,
1734+
},
1735+
},
1736+
)
1737+
cents := mintedAssets[0]
1738+
assetID := cents.AssetGenesis.AssetId
1739+
var groupKey []byte
1740+
if cents.AssetGroup != nil {
1741+
groupKey = cents.AssetGroup.TweakedGroupKey
1742+
}
1743+
fundingScriptTree := tapchannel.NewFundingScriptTree()
1744+
fundingScriptKey := fundingScriptTree.TaprootKey
1745+
fundingScriptTreeBytes := fundingScriptKey.SerializeCompressed()
1746+
1747+
t.Logf("Minted %d lightning cents, syncing universes...", cents.Amount)
1748+
syncUniverses(t.t, charlieTap, dave)
1749+
t.Logf("Universes synced between all nodes, distributing assets...")
1750+
1751+
charlieBalance := cents.Amount
1752+
1753+
fundRespCD, err := charlieTap.FundChannel(
1754+
ctxb, &tchrpc.FundChannelRequest{
1755+
AssetAmount: charlieBalance,
1756+
AssetId: assetID,
1757+
PeerPubkey: daveTap.node.PubKey[:],
1758+
FeeRateSatPerVbyte: 5,
1759+
PushSat: 0,
1760+
},
1761+
)
1762+
require.NoError(t.t, err)
1763+
t.Logf("Funded channel between Charlie and Dave: %v", fundRespCD)
1764+
1765+
// Make sure the pending channel shows up in the list and has the
1766+
// custom records set as JSON.
1767+
assertPendingChannels(
1768+
t.t, charlieTap.node, assetID, 1, charlieBalance, 0,
1769+
)
1770+
1771+
// Let's confirm the channel.
1772+
mineBlocks(t, net, 6, 1)
1773+
1774+
assertAssetBalance(t.t, charlieTap, assetID, cents.Amount)
1775+
1776+
// There should only be a single asset piece for Charlie, the one in the
1777+
// channel.
1778+
assertNumAssetOutputs(t.t, charlieTap, assetID, 1)
1779+
assertAssetExists(
1780+
t.t, charlieTap, assetID, charlieBalance,
1781+
fundingScriptKey, false, true, true,
1782+
)
1783+
1784+
// Assert that the proofs for both channels has been uploaded to the
1785+
// designated Universe server.
1786+
assertUniverseProofExists(
1787+
t.t, universeTap, assetID, groupKey, fundingScriptTreeBytes,
1788+
fmt.Sprintf("%v:%v", fundRespCD.Txid, fundRespCD.OutputIndex),
1789+
)
1790+
1791+
// Make sure the channel shows the correct asset information.
1792+
assertAssetChan(
1793+
t.t, charlieTap.node, daveTap.node, charlieBalance, assetID,
1794+
)
1795+
1796+
logBalance(t.t, nodes, assetID, "initial")
1797+
1798+
// Normal case.
1799+
// Send 50 assets from Charlie to Dave.
1800+
sendAssetKeySendPayment(
1801+
t.t, charlie, dave, 50, assetID,
1802+
fn.None[int64](), lnrpc.Payment_SUCCEEDED,
1803+
)
1804+
1805+
logBalance(t.t, nodes, assetID, "after 50 assets")
1806+
1807+
// Normal case.
1808+
// Send 1k sats from Charlie to Dave.
1809+
sendKeySendPayment(t.t, charlie, dave, 1000)
1810+
1811+
logBalance(t.t, nodes, assetID, "after 1k sats")
1812+
1813+
// Edge case: The channel reserve check should trigger, and we should
1814+
// get a payment failure, not a timeout.
1815+
//
1816+
// Now Dave tries to send 50 assets to Charlie. There shouldn't be
1817+
// enough sats in the channel.
1818+
sendAssetKeySendPayment(
1819+
t.t, dave, charlie, 50, assetID,
1820+
fn.None[int64](), lnrpc.Payment_FAILED,
1821+
)
1822+
1823+
logBalance(t.t, nodes, assetID, "after failed 50 assets")
1824+
1825+
// Send 10k sats from Charlie to Dave.
1826+
sendKeySendPayment(t.t, charlie, dave, 10000)
1827+
1828+
logBalance(t.t, nodes, assetID, "10k sats")
1829+
1830+
// Now Dave tries to send 50 assets again, this time he should have
1831+
// enough sats.
1832+
sendAssetKeySendPayment(
1833+
t.t, dave, charlie, 50, assetID,
1834+
fn.None[int64](), lnrpc.Payment_SUCCEEDED,
1835+
)
1836+
1837+
logBalance(t.t, nodes, assetID, "after 50 sats backwards")
1838+
1839+
// Edge case: This refers to a bug where an asset allocation would be
1840+
// expected for this HTLC. This is a dust HTLC and it can not carry
1841+
// assets.
1842+
//
1843+
// Send 1 sat from Charlie to Dave.
1844+
sendKeySendPayment(t.t, charlie, dave, 1)
1845+
1846+
logBalance(t.t, nodes, assetID, "after 1 sat")
1847+
1848+
// Pay a normal bolt11 invoice involving RFQ flow.
1849+
_ = createAndPayNormalInvoice(
1850+
t.t, charlie, dave, erin, 20_000, assetID, true,
1851+
)
1852+
1853+
logBalance(t.t, nodes, assetID, "after 20k sat asset payment")
1854+
1855+
// Edge case: There was a bug when paying an asset invoice that would
1856+
// evaluate to more than the channel capacity, causing a payment failure
1857+
// even though enough asset balance exists.
1858+
//
1859+
// Pay a bolt11 invoice with assets, which evaluates to more than the
1860+
// channel btc capacity.
1861+
_ = createAndPayNormalInvoice(
1862+
t.t, charlie, dave, erin, 220_000, assetID, true,
1863+
)
1864+
1865+
logBalance(t.t, nodes, assetID, "after giant asset payment")
1866+
}

itest/litd_test_list_on_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,8 @@ var allTestCases = []*testCase{
4040
name: "test custom channels breach",
4141
test: testCustomChannelsBreach,
4242
},
43+
{
44+
name: "test custom channels liquidity",
45+
test: testCustomChannelsLiquidityEdgeCases,
46+
},
4347
}

0 commit comments

Comments
 (0)