@@ -2302,6 +2302,237 @@ func testCustomChannelsBreach(ctx context.Context, net *NetworkHarness,
2302
2302
t .Logf ("Charlie balance after breach: %d" , charlieBalance )
2303
2303
}
2304
2304
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
+
2305
2536
// testCustomChannelsLiquidityEdgeCasesCore is the core logic of the liquidity
2306
2537
// edge cases. This test goes through certain scenarios that expose edge cases
2307
2538
// and behaviors that proved to be buggy in the past and have been directly
0 commit comments