@@ -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
0 commit comments