@@ -14,6 +14,7 @@ import (
1414	"github.com/btcsuite/btcd/btcec/v2/schnorr" 
1515	"github.com/btcsuite/btcd/btcutil" 
1616	"github.com/btcsuite/btcd/chaincfg/chainhash" 
17+ 	"github.com/btcsuite/btcd/wire" 
1718	"github.com/lightninglabs/taproot-assets/asset" 
1819	"github.com/lightninglabs/taproot-assets/itest" 
1920	"github.com/lightninglabs/taproot-assets/proof" 
@@ -2302,6 +2303,318 @@ func testCustomChannelsBreach(ctx context.Context, net *NetworkHarness,
23022303	t .Logf ("Charlie balance after breach: %d" , charlieBalance )
23032304}
23042305
2306+ // testCustomChannelsV1Upgrade tests the upgrade path of a taproot assets 
2307+ // channel. It upgrades one of the peers to a version that utilizes feature bits 
2308+ // and new features over the channel, testing that backwards compatibility is 
2309+ // maintained along the way. We also introduce a channel breach, right at the 
2310+ // point before we switched over to the new features, to test that sweeping is 
2311+ // done properly. 
2312+ func  testCustomChannelsV1Upgrade (ctx  context.Context , net  * NetworkHarness ,
2313+ 	t  * harnessTest ) {
2314+ 
2315+ 	lndArgs  :=  slices .Clone (lndArgsTemplate )
2316+ 	litdArgs  :=  slices .Clone (litdArgsTemplate )
2317+ 
2318+ 	zane , err  :=  net .NewNode (
2319+ 		t .t , "Zane" , lndArgs , false , true , litdArgs ... ,
2320+ 	)
2321+ 	require .NoError (t .t , err )
2322+ 
2323+ 	litdArgs  =  append (litdArgs , fmt .Sprintf (
2324+ 		"--taproot-assets.proofcourieraddr=%s://%s" ,
2325+ 		proof .UniverseRpcCourierType , zane .Cfg .LitAddr (),
2326+ 	))
2327+ 
2328+ 	davePort  :=  port .NextAvailablePort ()
2329+ 	daveFlags  :=  append (
2330+ 		slices .Clone (lndArgs ), "--nolisten" , "--minbackoff=1h" ,
2331+ 	)
2332+ 
2333+ 	// For this simple test, we'll just have Charlie -> Dave as an assets 
2334+ 	// channel. 
2335+ 	dave , err  :=  net .NewNodeWithPort (
2336+ 		t .t , "Dave" , daveFlags , false , true , davePort ,
2337+ 		litdArgs ... ,
2338+ 	)
2339+ 	require .NoError (t .t , err )
2340+ 
2341+ 	charlie , err  :=  net .NewNode (t .t , "Charlie" , lndArgs , false , true , litdArgs ... )
2342+ 	require .NoError (t .t , err )
2343+ 
2344+ 	// Next we'll connect all the nodes and also fund them with some coins. 
2345+ 	nodes  :=  []* HarnessNode {dave , charlie }
2346+ 	connectAllNodes (t .t , net , nodes )
2347+ 	fundAllNodes (t .t , net , nodes )
2348+ 
2349+ 	universeTap  :=  newTapClient (t .t , zane )
2350+ 	charlieTap  :=  newTapClient (t .t , charlie )
2351+ 	daveTap  :=  newTapClient (t .t , dave )
2352+ 
2353+ 	// Now we'll make an asset for Charlie that we'll use in the test to 
2354+ 	// open a channel. 
2355+ 	mintedAssets  :=  itest .MintAssetsConfirmBatch (
2356+ 		t .t , t .lndHarness .Miner .Client , charlieTap ,
2357+ 		[]* mintrpc.MintAssetRequest {
2358+ 			{
2359+ 				Asset : itestAsset ,
2360+ 			},
2361+ 		},
2362+ 	)
2363+ 	cents  :=  mintedAssets [0 ]
2364+ 	assetID  :=  cents .AssetGenesis .AssetId 
2365+ 
2366+ 	t .Logf ("Minted %d itest asset cents, syncing universes..." ,
2367+ 		cents .Amount )
2368+ 
2369+ 	syncUniverses (t .t , charlieTap , dave )
2370+ 	t .Logf ("Universes synced between all nodes, distributing assets..." )
2371+ 
2372+ 	// Next we can open an asset channel from Charlie -> Dave, then kick 
2373+ 	// off the main scenario. 
2374+ 	t .Logf ("Opening asset channels..." )
2375+ 	assetFundResp , err  :=  charlieTap .FundChannel (
2376+ 		ctx , & tchrpc.FundChannelRequest {
2377+ 			AssetAmount :        fundingAmount ,
2378+ 			AssetId :            assetID ,
2379+ 			PeerPubkey :         dave .PubKey [:],
2380+ 			FeeRateSatPerVbyte : 5 ,
2381+ 		},
2382+ 	)
2383+ 	require .NoError (t .t , err )
2384+ 	t .Logf ("Funded channel between Charlie and Dave: %v" , assetFundResp )
2385+ 
2386+ 	// With the channel open, mine 6 blocks to confirm it. 
2387+ 	mineBlocks (t , net , 6 , 1 )
2388+ 
2389+ 	// A transfer for the funding transaction should be found in Charlie's 
2390+ 	// DB. 
2391+ 	fundingTxid , err  :=  chainhash .NewHashFromStr (assetFundResp .Txid )
2392+ 	require .NoError (t .t , err )
2393+ 	assetFundingTransfer  :=  locateAssetTransfers (
2394+ 		t .t , charlieTap , * fundingTxid ,
2395+ 	)
2396+ 
2397+ 	t .Logf ("Channel funding transfer: %v" ,
2398+ 		toProtoJSON (t .t , assetFundingTransfer ))
2399+ 
2400+ 	// Charlie's balance should reflect that the funding asset is now 
2401+ 	// excluded from balance reporting by tapd. 
2402+ 	itest .AssertBalances (
2403+ 		t .t , charlieTap , itestAsset .Amount - fundingAmount ,
2404+ 		itest .WithAssetID (assetID ), itest .WithNumUtxos (1 ),
2405+ 	)
2406+ 
2407+ 	// Make sure that Charlie properly uploaded funding proof to the 
2408+ 	// Universe server. 
2409+ 	fundingScriptTree  :=  tapscript .NewChannelFundingScriptTree ()
2410+ 	fundingScriptKey  :=  fundingScriptTree .TaprootKey 
2411+ 	fundingScriptTreeBytes  :=  fundingScriptKey .SerializeCompressed ()
2412+ 	assertUniverseProofExists (
2413+ 		t .t , universeTap , assetID , nil , fundingScriptTreeBytes ,
2414+ 		fmt .Sprintf (
2415+ 			"%v:%v" , assetFundResp .Txid , assetFundResp .OutputIndex ,
2416+ 		),
2417+ 	)
2418+ 
2419+ 	// Make sure the channel shows the correct asset information. 
2420+ 	assertAssetChan (
2421+ 		t .t , charlie , dave , fundingAmount , []* taprpc.Asset {cents },
2422+ 	)
2423+ 
2424+ 	// Before we start sending out payments, let's make sure each node can 
2425+ 	// see the other one in the graph and has all required features. 
2426+ 	require .NoError (t .t , t .lndHarness .AssertNodeKnown (charlie , dave ))
2427+ 	require .NoError (t .t , t .lndHarness .AssertNodeKnown (dave , charlie ))
2428+ 
2429+ 	logBalance (t .t , nodes , assetID , "start" )
2430+ 
2431+ 	// Let's dispatch 5 asset & 5 keysend payments from Charlie to Dave. At 
2432+ 	// this point Charlie is running the old version of LiT. 
2433+ 	for  range  5  {
2434+ 		sendAssetKeySendPayment (
2435+ 			t .t , charlie , dave , 50 , assetID , fn .None [int64 ](),
2436+ 		)
2437+ 		sendKeySendPayment (t .t , charlie , dave , 1_000 )
2438+ 	}
2439+ 
2440+ 	logBalance (t .t , nodes , assetID , "before upgrade" )
2441+ 
2442+ 	// Let's assert that Charlie & Dave actually run different versions of 
2443+ 	// taproot-assets. We expect Dave to be running the latest version, 
2444+ 	// while Charlie is running an older version (v0.15.0). 
2445+ 	daveInfo , err  :=  daveTap .GetInfo (ctx , & taprpc.GetInfoRequest {})
2446+ 	require .NoError (t .t , err )
2447+ 
2448+ 	charlieInfo , err  :=  charlieTap .GetInfo (ctx , & taprpc.GetInfoRequest {})
2449+ 	require .NoError (t .t , err )
2450+ 
2451+ 	require .NotEqual (t .t , daveInfo .Version , charlieInfo .Version )
2452+ 
2453+ 	res , err  :=  charlie .ChannelBalance (ctx , & lnrpc.ChannelBalanceRequest {})
2454+ 	require .NoError (t .t , err )
2455+ 
2456+ 	charlieSatsBefore  :=  res .LocalBalance 
2457+ 
2458+ 	// Now we'll restart Charlie and assert that he upgraded. We also back 
2459+ 	// up the DB at this point, in order to induce a breach later right at 
2460+ 	// the switching point before upgrading the channel. We will verify that 
2461+ 	// the breach transaction will be swept by the right party. 
2462+ 	require .NoError (t .t , net .StopAndBackupDB (charlie , WithUpgrade ()))
2463+ 	connectAllNodes (t .t , net , nodes )
2464+ 
2465+ 	charlieInfo , err  =  charlieTap .GetInfo (ctx , & taprpc.GetInfoRequest {})
2466+ 	require .NoError (t .t , err )
2467+ 
2468+ 	// Dave and Charlie should both be running the same version (latest). 
2469+ 	require .Equal (t .t , daveInfo .Version , charlieInfo .Version )
2470+ 
2471+ 	// Let's send another 5 asset and keysend payments from Charlie to Dave. 
2472+ 	// Charlie is now on the latest version of LiT and the channel upgraded. 
2473+ 	for  range  5  {
2474+ 		sendAssetKeySendPayment (
2475+ 			t .t , charlie , dave , 50 , assetID , fn .None [int64 ](),
2476+ 		)
2477+ 	}
2478+ 
2479+ 	res , err  =  charlie .ChannelBalance (ctx , & lnrpc.ChannelBalanceRequest {})
2480+ 	require .NoError (t .t , err )
2481+ 
2482+ 	charlieSatsAfter  :=  res .LocalBalance 
2483+ 
2484+ 	// Because of no-op HTLCs, the satoshi balance of Charlie should not 
2485+ 	// have shifted while sending the asset payments. 
2486+ 	require .Equal (t .t , charlieSatsBefore , charlieSatsAfter )
2487+ 
2488+ 	logBalance (t .t , nodes , assetID , "after upgrade" )
2489+ 
2490+ 	// Now let's restart Charlie and restore the DB to the previous snapshot 
2491+ 	// which corresponds to a previous (invalid) and unupgraded channel 
2492+ 	// state. 
2493+ 	require .NoError (t .t , net .StopAndRestoreDB (charlie ))
2494+ 
2495+ 	// With Charlie restored, we'll now execute the force close. 
2496+ 	t .Logf ("Force close by Charlie to breach..." )
2497+ 	charlieChanPoint  :=  & lnrpc.ChannelPoint {
2498+ 		OutputIndex : uint32 (assetFundResp .OutputIndex ),
2499+ 		FundingTxid : & lnrpc.ChannelPoint_FundingTxidStr {
2500+ 			FundingTxidStr : assetFundResp .Txid ,
2501+ 		},
2502+ 	}
2503+ 	_ , breachTxid , err  :=  net .CloseChannel (charlie , charlieChanPoint , true )
2504+ 	require .NoError (t .t , err )
2505+ 
2506+ 	t .Logf ("Channel closed! Mining blocks, close_txid=%v" , breachTxid )
2507+ 
2508+ 	// Next, we'll mine a block to confirm the breach transaction. 
2509+ 	mineBlocks (t , net , 1 , 1 )
2510+ 
2511+ 	// We should be able to find the transfer of the breach for both 
2512+ 	// parties. 
2513+ 	charlieBreachTransfer  :=  locateAssetTransfers (
2514+ 		t .t , charlieTap , * breachTxid ,
2515+ 	)
2516+ 	daveBreachTransfer  :=  locateAssetTransfers (
2517+ 		t .t , daveTap , * breachTxid ,
2518+ 	)
2519+ 
2520+ 	t .Logf ("Charlie breach transfer: %v" ,
2521+ 		toProtoJSON (t .t , charlieBreachTransfer ))
2522+ 	t .Logf ("Dave breach transfer: %v" ,
2523+ 		toProtoJSON (t .t , daveBreachTransfer ))
2524+ 
2525+ 	require .Len (t .t , charlieBreachTransfer .Outputs , 2 )
2526+ 	assetOutput  :=  charlieBreachTransfer .Outputs [0 ]
2527+ 	assertUniverseProofExists (
2528+ 		t .t , universeTap , assetID , nil , assetOutput .ScriptKey ,
2529+ 		assetOutput .Anchor .Outpoint ,
2530+ 	)
2531+ 
2532+ 	op , err  :=  wire .NewOutPointFromString (assetOutput .Anchor .Outpoint )
2533+ 	require .NoError (t .t , err )
2534+ 
2535+ 	// We'll manually export the proof of the breach transfer, in order to 
2536+ 	// verify that it indeed did not use STXO proofs. 
2537+ 	proofResp , err  :=  daveTap .ExportProof (ctx , & taprpc.ExportProofRequest {
2538+ 		AssetId :   assetID ,
2539+ 		ScriptKey : assetOutput .ScriptKey ,
2540+ 		Outpoint : & taprpc.OutPoint {
2541+ 			Txid :        op .Hash [:],
2542+ 			OutputIndex : op .Index ,
2543+ 		},
2544+ 	})
2545+ 	require .NoError (t .t , err )
2546+ 
2547+ 	proofFile , err  :=  proof .DecodeFile (proofResp .RawProofFile )
2548+ 	require .NoError (t .t , err )
2549+ 	require .Equal (t .t , proofFile .NumProofs (), 3 )
2550+ 	latestProof , err  :=  proofFile .LastProof ()
2551+ 	require .NoError (t .t , err )
2552+ 
2553+ 	// This proof should not contain the STXO exclusion proofs, since the 
2554+ 	// breach occured right before the channel upgraded. 
2555+ 	stxoProofs  :=  latestProof .ExclusionProofs [0 ].CommitmentProof .STXOProofs 
2556+ 	require .Nil (t .t , stxoProofs )
2557+ 
2558+ 	// With the breach transaction mined, Dave should now have a transaction 
2559+ 	// in the mempool sweeping *both* commitment outputs. 
2560+ 	daveJusticeTxid , err  :=  waitForNTxsInMempool (
2561+ 		net .Miner .Client , 1 , time .Second * 5 ,
2562+ 	)
2563+ 	require .NoError (t .t , err )
2564+ 
2565+ 	t .Logf ("Dave justice txid: %v" , daveJusticeTxid )
2566+ 
2567+ 	// Next, we'll mine a block to confirm Dave's justice transaction. 
2568+ 	mineBlocks (t , net , 1 , 1 )
2569+ 
2570+ 	// Dave should now have a transfer for his justice transaction. 
2571+ 	daveJusticeTransfer  :=  locateAssetTransfers (
2572+ 		t .t , daveTap , * daveJusticeTxid [0 ],
2573+ 	)
2574+ 
2575+ 	t .Logf ("Dave justice transfer: %v" ,
2576+ 		toProtoJSON (t .t , daveJusticeTransfer ))
2577+ 
2578+ 	// Dave should claim all of the asset balance that was put into the 
2579+ 	// channel. 
2580+ 	daveBalance  :=  uint64 (fundingAmount )
2581+ 
2582+ 	itest .AssertBalances (
2583+ 		t .t , daveTap , daveBalance , itest .WithAssetID (assetID ),
2584+ 		itest .WithNumUtxos (2 ),
2585+ 	)
2586+ 
2587+ 	t .Logf ("Dave balance after breach: %d" , daveBalance )
2588+ 
2589+ 	require .Len (t .t , daveJusticeTransfer .Outputs , 2 )
2590+ 	assetOutput  =  daveJusticeTransfer .Outputs [0 ]
2591+ 	op , err  =  wire .NewOutPointFromString (assetOutput .Anchor .Outpoint )
2592+ 	require .NoError (t .t , err )
2593+ 
2594+ 	// We'll now also export the proof for the justice transaction. Here we 
2595+ 	// expect to find STXO proofs, as the sweeping party is an upgraded node 
2596+ 	// that supports it. 
2597+ 	proofResp , err  =  daveTap .ExportProof (ctx , & taprpc.ExportProofRequest {
2598+ 		AssetId :   assetID ,
2599+ 		ScriptKey : assetOutput .ScriptKey ,
2600+ 		Outpoint : & taprpc.OutPoint {
2601+ 			Txid :        op .Hash [:],
2602+ 			OutputIndex : op .Index ,
2603+ 		},
2604+ 	})
2605+ 	require .NoError (t .t , err )
2606+ 
2607+ 	proofFile , err  =  proof .DecodeFile (proofResp .RawProofFile )
2608+ 	require .NoError (t .t , err )
2609+ 	require .Equal (t .t , 4 , proofFile .NumProofs ())
2610+ 	latestProof , err  =  proofFile .LastProof ()
2611+ 	require .NoError (t .t , err )
2612+ 
2613+ 	// This proof should contain the STXO exclusion proofs 
2614+ 	stxoProofs  =  latestProof .InclusionProof .CommitmentProof .STXOProofs 
2615+ 	require .NotNil (t .t , stxoProofs )
2616+ }
2617+ 
23052618// testCustomChannelsLiquidityEdgeCasesCore is the core logic of the liquidity 
23062619// edge cases. This test goes through certain scenarios that expose edge cases 
23072620// and behaviors that proved to be buggy in the past and have been directly 
0 commit comments