Skip to content

Commit e626dfa

Browse files
committed
itest: add custom channels v1 upgrade itest
1 parent 81532ac commit e626dfa

File tree

2 files changed

+239
-0
lines changed

2 files changed

+239
-0
lines changed

itest/litd_custom_channels_test.go

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2302,6 +2302,237 @@ func testCustomChannelsBreach(ctx context.Context, net *NetworkHarness,
23022302
t.Logf("Charlie balance after breach: %d", charlieBalance)
23032303
}
23042304

2305+
// testCustomChannelsV1Upgrade tests the upgrade path of a taproot assets
2306+
// channel. It upgrades one of the peers to a version that utilizes feature bits
2307+
// and new features over the channel, testing that backwards compatibility is
2308+
// maintained along the way. We also introduce a channel breach, right at the
2309+
// point before we switched over to the new features, to test that sweeping is
2310+
// done properly.
2311+
func testCustomChannelsV1Upgrade(ctx context.Context, net *NetworkHarness,
2312+
t *harnessTest) {
2313+
2314+
lndArgs := slices.Clone(lndArgsTemplate)
2315+
litdArgs := slices.Clone(litdArgsTemplate)
2316+
2317+
davePort := port.NextAvailablePort()
2318+
litdArgs = append(litdArgs, fmt.Sprintf(
2319+
"--taproot-assets.proofcourieraddr=%s://%s",
2320+
proof.UniverseRpcCourierType,
2321+
fmt.Sprintf(node.ListenerFormat, davePort),
2322+
))
2323+
2324+
daveFlags := append(
2325+
slices.Clone(lndArgs), "--nolisten", "--minbackoff=1h",
2326+
)
2327+
2328+
// For this simple test, we'll just have Carol -> Dave as an assets
2329+
// channel.
2330+
dave, err := net.NewNodeWithPort(
2331+
t.t, "Dave", daveFlags, false, true, davePort,
2332+
litdArgs...,
2333+
)
2334+
require.NoError(t.t, err)
2335+
2336+
charlie, err := net.NewNode(t.t, "Charlie", lndArgs, false, true, litdArgs...)
2337+
require.NoError(t.t, err)
2338+
2339+
// Next we'll connect all the nodes and also fund them with some coins.
2340+
nodes := []*HarnessNode{dave, charlie}
2341+
connectAllNodes(t.t, net, nodes)
2342+
fundAllNodes(t.t, net, nodes)
2343+
2344+
universeTap := newTapClient(t.t, dave)
2345+
charlieTap := newTapClient(t.t, charlie)
2346+
daveTap := newTapClient(t.t, dave)
2347+
2348+
// Now we'll make an asset for Charlie that we'll use in the test to
2349+
// open a channel.
2350+
mintedAssets := itest.MintAssetsConfirmBatch(
2351+
t.t, t.lndHarness.Miner.Client, charlieTap,
2352+
[]*mintrpc.MintAssetRequest{
2353+
{
2354+
Asset: itestAsset,
2355+
},
2356+
},
2357+
)
2358+
cents := mintedAssets[0]
2359+
assetID := cents.AssetGenesis.AssetId
2360+
2361+
t.Logf("Minted %d lightning cents, syncing universes...", cents.Amount)
2362+
syncUniverses(t.t, charlieTap, dave)
2363+
t.Logf("Universes synced between all nodes, distributing assets...")
2364+
2365+
// Next we can open an asset channel from Charlie -> Dave, then kick
2366+
// off the main scenario.
2367+
t.Logf("Opening asset channels...")
2368+
assetFundResp, err := charlieTap.FundChannel(
2369+
ctx, &tchrpc.FundChannelRequest{
2370+
AssetAmount: fundingAmount,
2371+
AssetId: assetID,
2372+
PeerPubkey: dave.PubKey[:],
2373+
FeeRateSatPerVbyte: 5,
2374+
},
2375+
)
2376+
require.NoError(t.t, err)
2377+
t.Logf("Funded channel between Charlie and Dave: %v", assetFundResp)
2378+
2379+
// With the channel open, mine a block to confirm it.
2380+
mineBlocks(t, net, 6, 1)
2381+
2382+
// A transfer for the funding transaction should be found in Charlie's
2383+
// DB.
2384+
fundingTxid, err := chainhash.NewHashFromStr(assetFundResp.Txid)
2385+
require.NoError(t.t, err)
2386+
assetFundingTransfer := locateAssetTransfers(
2387+
t.t, charlieTap, *fundingTxid,
2388+
)
2389+
2390+
t.Logf("Channel funding transfer: %v",
2391+
toProtoJSON(t.t, assetFundingTransfer))
2392+
2393+
// Charlie's balance should reflect that the funding asset is now
2394+
// excluded from balance reporting by tapd.
2395+
itest.AssertBalances(
2396+
t.t, charlieTap, itestAsset.Amount-fundingAmount,
2397+
itest.WithAssetID(assetID), itest.WithNumUtxos(1),
2398+
)
2399+
2400+
// Make sure that Charlie properly uploaded funding proof to the
2401+
// Universe server.
2402+
fundingScriptTree := tapscript.NewChannelFundingScriptTree()
2403+
fundingScriptKey := fundingScriptTree.TaprootKey
2404+
fundingScriptTreeBytes := fundingScriptKey.SerializeCompressed()
2405+
assertUniverseProofExists(
2406+
t.t, universeTap, assetID, nil, fundingScriptTreeBytes,
2407+
fmt.Sprintf(
2408+
"%v:%v", assetFundResp.Txid, assetFundResp.OutputIndex,
2409+
),
2410+
)
2411+
2412+
// Make sure the channel shows the correct asset information.
2413+
assertAssetChan(
2414+
t.t, charlie, dave, fundingAmount, []*taprpc.Asset{cents},
2415+
)
2416+
2417+
// Before we start sending out payments, let's make sure each node can
2418+
// see the other one in the graph and has all required features.
2419+
require.NoError(t.t, t.lndHarness.AssertNodeKnown(charlie, dave))
2420+
require.NoError(t.t, t.lndHarness.AssertNodeKnown(dave, charlie))
2421+
2422+
logBalance(t.t, nodes, assetID, "start")
2423+
2424+
// Let's dispatch 5 asset & 5 keysend payments from Charlie to Dave. At
2425+
// this point Charlie is running the old version of LiT.
2426+
for range 5 {
2427+
sendAssetKeySendPayment(
2428+
t.t, charlie, dave, 50, assetID, fn.None[int64](),
2429+
)
2430+
sendKeySendPayment(t.t, charlie, dave, 1_000)
2431+
}
2432+
2433+
logBalance(t.t, nodes, assetID, "before upgrade")
2434+
2435+
// Let's assert that Charlie & Dave actually run different versions of
2436+
// taproot-assets.
2437+
daveInfo, err := daveTap.GetInfo(ctx, &taprpc.GetInfoRequest{})
2438+
require.NoError(t.t, err)
2439+
2440+
charlieInfo, err := charlieTap.GetInfo(ctx, &taprpc.GetInfoRequest{})
2441+
require.NoError(t.t, err)
2442+
2443+
require.NotEqual(t.t, daveInfo.Version, charlieInfo.Version)
2444+
2445+
// Now we'll restart Charlie and assert that he upgraded. We also back
2446+
// up the DB at this point, in order to induce a breach later right at
2447+
// the switching point before upgrading the channel. We will verify that
2448+
// the breach transaction will be swept by the right party.
2449+
require.NoError(t.t, net.StopAndBackupDB(charlie, WithUpgrade()))
2450+
connectAllNodes(t.t, net, nodes)
2451+
2452+
charlieInfo, err = charlieTap.GetInfo(ctx, &taprpc.GetInfoRequest{})
2453+
require.NoError(t.t, err)
2454+
2455+
// Dave and Charlie should both be running the same version (latest).
2456+
require.Equal(t.t, daveInfo.Version, charlieInfo.Version)
2457+
2458+
// Let's send another 5 asset and keysend payments from Charlie to Dave.
2459+
// Charlie is now on the latest version of LiT and the channel upgraded.
2460+
for range 5 {
2461+
sendAssetKeySendPayment(
2462+
t.t, charlie, dave, 50, assetID, fn.None[int64](),
2463+
)
2464+
sendKeySendPayment(t.t, charlie, dave, 1_000)
2465+
}
2466+
2467+
logBalance(t.t, nodes, assetID, "after upgrade")
2468+
2469+
// Now let's restart Charlie and restore the DB to the previous snapshot
2470+
// which corresponds to a previous (invalid) and unupgraded channel
2471+
// state.
2472+
require.NoError(t.t, net.StopAndRestoreDB(charlie))
2473+
2474+
// With Charlie restored, we'll now execute the force close.
2475+
t.Logf("Force close by Charlie to breach...")
2476+
charlieChanPoint := &lnrpc.ChannelPoint{
2477+
OutputIndex: uint32(assetFundResp.OutputIndex),
2478+
FundingTxid: &lnrpc.ChannelPoint_FundingTxidStr{
2479+
FundingTxidStr: assetFundResp.Txid,
2480+
},
2481+
}
2482+
_, breachTxid, err := net.CloseChannel(charlie, charlieChanPoint, true)
2483+
require.NoError(t.t, err)
2484+
2485+
t.Logf("Channel closed! Mining blocks, close_txid=%v", breachTxid)
2486+
2487+
// Next, we'll mine a block to confirm the breach transaction.
2488+
mineBlocks(t, net, 1, 1)
2489+
2490+
// We should be able to find the transfer of the breach for both
2491+
// parties.
2492+
charlieBreachTransfer := locateAssetTransfers(
2493+
t.t, charlieTap, *breachTxid,
2494+
)
2495+
daveBreachTransfer := locateAssetTransfers(
2496+
t.t, daveTap, *breachTxid,
2497+
)
2498+
2499+
t.Logf("Charlie breach transfer: %v",
2500+
toProtoJSON(t.t, charlieBreachTransfer))
2501+
t.Logf("Dave breach transfer: %v",
2502+
toProtoJSON(t.t, daveBreachTransfer))
2503+
2504+
// With the breach transaction mined, Dave should now have a transaction
2505+
// in the mempool sweeping *both* commitment outputs.
2506+
daveJusticeTxid, err := waitForNTxsInMempool(
2507+
net.Miner.Client, 1, time.Second*5,
2508+
)
2509+
require.NoError(t.t, err)
2510+
2511+
t.Logf("Dave justice txid: %v", daveJusticeTxid)
2512+
2513+
// Next, we'll mine a block to confirm Charlie's justice transaction.
2514+
mineBlocks(t, net, 1, 1)
2515+
2516+
// Dave should now have a transfer for his justice transaction.
2517+
daveJusticeTransfer := locateAssetTransfers(
2518+
t.t, daveTap, *daveJusticeTxid[0],
2519+
)
2520+
2521+
t.Logf("Dave justice transfer: %v",
2522+
toProtoJSON(t.t, daveJusticeTransfer))
2523+
2524+
// Dave should claim all of the asset balance that was put into the
2525+
// channel.
2526+
daveBalance := uint64(fundingAmount)
2527+
2528+
itest.AssertBalances(
2529+
t.t, daveTap, daveBalance, itest.WithAssetID(assetID),
2530+
itest.WithNumUtxos(2),
2531+
)
2532+
2533+
t.Logf("Dave balance after breach: %d", daveBalance)
2534+
}
2535+
23052536
// testCustomChannelsLiquidityEdgeCasesCore is the core logic of the liquidity
23062537
// edge cases. This test goes through certain scenarios that expose edge cases
23072538
// and behaviors that proved to be buggy in the past and have been directly

itest/litd_test_list_on_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,12 @@ var allTestCases = []*testCase{
150150
"Alice": "v0.14.1-alpha",
151151
},
152152
},
153+
{
154+
name: "custom channels v1 upgrade",
155+
test: testCustomChannelsV1Upgrade,
156+
noAliceBob: true,
157+
backwardCompat: map[string]string{
158+
"Charlie": "v0.15.0-alpha",
159+
},
160+
},
153161
}

0 commit comments

Comments
 (0)