@@ -61,8 +61,8 @@ func createTestAssetNetwork(t *harnessTest, net *NetworkHarness, charlieTap,
6161	daveTap , erinTap , fabiaTap , yaraTap , universeTap  * tapClient ,
6262	mintedAsset  * taprpc.Asset , assetSendAmount , charlieFundingAmount ,
6363	daveFundingAmount ,
64- 	erinFundingAmount  uint64 , pushSat  int64 ) (* tchrpc. FundChannelResponse ,
65- 	* tchrpc. FundChannelResponse , * tchrpc. FundChannelResponse ) {
64+ 	erinFundingAmount  uint64 , pushSat  int64 ) (* lnrpc. ChannelPoint ,
65+ 	* lnrpc. ChannelPoint , * lnrpc. ChannelPoint ) {
6666
6767	ctxb  :=  context .Background ()
6868	assetID  :=  mintedAsset .AssetGenesis .AssetId 
@@ -256,7 +256,26 @@ func createTestAssetNetwork(t *harnessTest, net *NetworkHarness, charlieTap,
256256		t .t , erinTap .node , fabiaTap .node , erinFundingAmount , assetID ,
257257	)
258258
259- 	return  fundRespCD , fundRespDY , fundRespEF 
259+ 	chanPointCD  :=  & lnrpc.ChannelPoint {
260+ 		OutputIndex : uint32 (fundRespCD .OutputIndex ),
261+ 		FundingTxid : & lnrpc.ChannelPoint_FundingTxidStr {
262+ 			FundingTxidStr : fundRespCD .Txid ,
263+ 		},
264+ 	}
265+ 	chanPointDY  :=  & lnrpc.ChannelPoint {
266+ 		OutputIndex : uint32 (fundRespDY .OutputIndex ),
267+ 		FundingTxid : & lnrpc.ChannelPoint_FundingTxidStr {
268+ 			FundingTxidStr : fundRespDY .Txid ,
269+ 		},
270+ 	}
271+ 	chanPointEF  :=  & lnrpc.ChannelPoint {
272+ 		OutputIndex : uint32 (fundRespEF .OutputIndex ),
273+ 		FundingTxid : & lnrpc.ChannelPoint_FundingTxidStr {
274+ 			FundingTxidStr : fundRespEF .Txid ,
275+ 		},
276+ 	}
277+ 
278+ 	return  chanPointCD , chanPointDY , chanPointEF 
260279}
261280
262281func  assertNumAssetUTXOs (t  * testing.T , tapdClient  * tapClient ,
@@ -586,6 +605,67 @@ func getAssetChannelBalance(t *testing.T, node *HarnessNode, assetID []byte,
586605		balance .RemoteBalance .Sat 
587606}
588607
608+ func  fetchChannel (t  * testing.T , node  * HarnessNode ,
609+ 	chanPoint  * lnrpc.ChannelPoint ) * lnrpc.Channel  {
610+ 
611+ 	ctxb  :=  context .Background ()
612+ 	ctxt , cancel  :=  context .WithTimeout (ctxb , defaultTimeout )
613+ 	defer  cancel ()
614+ 
615+ 	channelResp , err  :=  node .ListChannels (ctxt , & lnrpc.ListChannelsRequest {
616+ 		ActiveOnly : true ,
617+ 	})
618+ 	require .NoError (t , err )
619+ 
620+ 	chanFundingHash , err  :=  lnrpc .GetChanPointFundingTxid (chanPoint )
621+ 	require .NoError (t , err )
622+ 
623+ 	chanPointStr  :=  fmt .Sprintf ("%v:%v" , chanFundingHash ,
624+ 		chanPoint .OutputIndex )
625+ 
626+ 	var  targetChan  * lnrpc.Channel 
627+ 	for  _ , channel  :=  range  channelResp .Channels  {
628+ 		if  channel .ChannelPoint  ==  chanPointStr  {
629+ 			targetChan  =  channel 
630+ 
631+ 			break 
632+ 		}
633+ 	}
634+ 	require .NotNil (t , targetChan )
635+ 
636+ 	return  targetChan 
637+ }
638+ 
639+ func  assertChannelSatBalance (t  * testing.T , node  * HarnessNode ,
640+ 	chanPoint  * lnrpc.ChannelPoint , local , remote  int64 ) {
641+ 
642+ 	targetChan  :=  fetchChannel (t , node , chanPoint )
643+ 
644+ 	require .InDelta (t , local , targetChan .LocalBalance , 1 )
645+ 	require .InDelta (t , remote , targetChan .RemoteBalance , 1 )
646+ }
647+ 
648+ func  assertChannelAssetBalance (t  * testing.T , node  * HarnessNode ,
649+ 	chanPoint  * lnrpc.ChannelPoint , local , remote  uint64 ) {
650+ 
651+ 	targetChan  :=  fetchChannel (t , node , chanPoint )
652+ 
653+ 	var  assetBalance  rfqmsg.JsonAssetChannel 
654+ 	err  :=  json .Unmarshal (targetChan .CustomChannelData , & assetBalance )
655+ 	require .NoError (t , err )
656+ 
657+ 	require .Len (t , assetBalance .Assets , 1 )
658+ 
659+ 	require .InDelta (t , local , assetBalance .Assets [0 ].LocalBalance , 1 )
660+ 	require .InDelta (t , remote , assetBalance .Assets [0 ].RemoteBalance , 1 )
661+ }
662+ 
663+ // addRoutingFee adds the default routing fee (1 part per million fee rate plus 
664+ // 1000 milli-satoshi base fee) to the given milli-satoshi amount. 
665+ func  addRoutingFee (amt  lnwire.MilliSatoshi ) lnwire.MilliSatoshi  {
666+ 	return  amt  +  (amt  /  1000_000 ) +  1000 
667+ }
668+ 
589669func  sendAssetKeySendPayment (t  * testing.T , src , dst  * HarnessNode , amt  uint64 ,
590670	assetID  []byte , btcAmt  fn.Option [int64 ],
591671	expectedStatus  lnrpc.Payment_PaymentStatus ,
@@ -702,9 +782,11 @@ func createAndPayNormalInvoice(t *testing.T, src, rfqPeer, dst *HarnessNode,
702782	})
703783	require .NoError (t , err )
704784
705- 	return  payInvoiceWithAssets (
785+ 	numUnits ,  _   :=  payInvoiceWithAssets (
706786		t , src , rfqPeer , invoiceResp , assetID , smallShards ,
707787	)
788+ 
789+ 	return  numUnits 
708790}
709791
710792func  payInvoiceWithSatoshi (t  * testing.T , payer  * HarnessNode ,
@@ -730,7 +812,7 @@ func payInvoiceWithSatoshi(t *testing.T, payer *HarnessNode,
730812
731813func  payInvoiceWithAssets (t  * testing.T , payer , rfqPeer  * HarnessNode ,
732814	invoice  * lnrpc.AddInvoiceResponse , assetID  []byte ,
733- 	smallShards  bool ) uint64  {
815+ 	smallShards  bool ) ( uint64 , rfqmath. BigIntFixedPoint )  {
734816
735817	ctxb  :=  context .Background ()
736818	ctxt , cancel  :=  context .WithTimeout (ctxb , defaultTimeout )
@@ -774,19 +856,21 @@ func payInvoiceWithAssets(t *testing.T, payer, rfqPeer *HarnessNode,
774856	rate , err  :=  rfqrpc .UnmarshalFixedPoint (rpcRate )
775857	require .NoError (t , err )
776858
859+ 	t .Logf ("Got quote for %v asset units per BTC" , rate )
860+ 
777861	amountMsat  :=  lnwire .MilliSatoshi (decodedInvoice .NumMsat )
778862	milliSatsFP  :=  rfqmath .MilliSatoshiToUnits (amountMsat , * rate )
779863	numUnits  :=  milliSatsFP .ScaleTo (0 ).ToUint64 ()
780- 	msatPerUnit  :=  uint64 (decodedInvoice .NumMsat ) /  numUnits 
781- 	t .Logf ("Got quote for %v asset units at %v  msat/unit from peer %s " + 
864+ 	msatPerUnit  :=  float64 (decodedInvoice .NumMsat ) /  float64 ( numUnits ) 
865+ 	t .Logf ("Got quote for %v asset units at %3f  msat/unit from peer %s " + 
782866		"with SCID %d" , numUnits , msatPerUnit , peerPubKey ,
783867		acceptedQuote .Scid )
784868
785869	result , err  :=  getAssetPaymentResult (stream )
786870	require .NoError (t , err )
787871	require .Equal (t , lnrpc .Payment_SUCCEEDED , result .Status )
788872
789- 	return  numUnits 
873+ 	return  numUnits ,  * rate 
790874}
791875
792876func  createAssetInvoice (t  * testing.T , dstRfqPeer , dst  * HarnessNode ,
@@ -825,19 +909,70 @@ func createAssetInvoice(t *testing.T, dstRfqPeer, dst *HarnessNode,
825909	rate , err  :=  rfqrpc .UnmarshalFixedPoint (rpcRate )
826910	require .NoError (t , err )
827911
912+ 	t .Logf ("Got quote for %v asset units per BTC" , rate )
913+ 
828914	assetUnits  :=  rfqmath .NewBigIntFixedPoint (assetAmount , 0 )
829915	numMSats  :=  rfqmath .UnitsToMilliSatoshi (assetUnits , * rate )
830- 	mSatPerUnit  :=  uint64 (decodedInvoice .NumMsat ) /  assetAmount 
916+ 	mSatPerUnit  :=  float64 (decodedInvoice .NumMsat ) /  float64 ( assetAmount ) 
831917
832918	require .EqualValues (t , numMSats , decodedInvoice .NumMsat )
833919
834- 	t .Logf ("Got quote for %d sats  at %v  msat/unit from peer %x with SCID  " + 
835- 		"%d" , decodedInvoice .NumMsat , mSatPerUnit ,  dstRfqPeer . PubKey [:] ,
836- 		resp .AcceptedBuyQuote .Scid )
920+ 	t .Logf ("Got quote for %d mSats  at %3f  msat/unit from peer %x with " + 
921+ 		"SCID  %d" , decodedInvoice .NumMsat , mSatPerUnit ,
922+ 		dstRfqPeer . PubKey [:],  resp .AcceptedBuyQuote .Scid )
837923
838924	return  resp .InvoiceResult 
839925}
840926
927+ // assertPaymentHtlcAssets makes sure the payment with the given hash shows the 
928+ // individual HTLCs that arrived for it and that they show the correct asset 
929+ // amounts for the given ID when decoded. 
930+ func  assertPaymentHtlcAssets (t  * testing.T , node  * HarnessNode , payHash  []byte ,
931+ 	assetID  []byte , assetAmount  uint64 ) {
932+ 
933+ 	ctxb  :=  context .Background ()
934+ 	ctxt , cancel  :=  context .WithTimeout (ctxb , defaultTimeout )
935+ 	defer  cancel ()
936+ 
937+ 	stream , err  :=  node .RouterClient .TrackPaymentV2 (
938+ 		ctxt , & routerrpc.TrackPaymentRequest {
939+ 			PaymentHash :       payHash ,
940+ 			NoInflightUpdates : true ,
941+ 		},
942+ 	)
943+ 	require .NoError (t , err )
944+ 
945+ 	payment , err  :=  stream .Recv ()
946+ 	require .NoError (t , err )
947+ 	require .NotNil (t , payment )
948+ 	require .NotEmpty (t , payment .Htlcs )
949+ 
950+ 	t .Logf ("Asset payment: %v" , toProtoJSON (t , payment ))
951+ 
952+ 	targetID  :=  hex .EncodeToString (assetID )
953+ 
954+ 	var  totalAssetAmount  uint64 
955+ 	for  _ , htlc  :=  range  payment .Htlcs  {
956+ 		require .NotNil (t , htlc .Route )
957+ 		require .NotEmpty (t , htlc .Route .CustomChannelData )
958+ 
959+ 		jsonHtlc  :=  & rfqmsg.JsonHtlc {}
960+ 		err  :=  json .Unmarshal (htlc .Route .CustomChannelData , jsonHtlc )
961+ 		require .NoError (t , err )
962+ 
963+ 		for  _ , balance  :=  range  jsonHtlc .Balances  {
964+ 			if  balance .AssetID  !=  targetID  {
965+ 				continue 
966+ 			}
967+ 
968+ 			totalAssetAmount  +=  balance .Amount 
969+ 		}
970+ 	}
971+ 
972+ 	// Due to rounding we allow up to 1 unit of error. 
973+ 	require .InDelta (t , assetAmount , totalAssetAmount , 1 )
974+ }
975+ 
841976func  waitForSendEvent (t  * testing.T ,
842977	sendEvents  taprpc.TaprootAssets_SubscribeSendEventsClient ,
843978	expectedState  tapfreighter.SendState ) {
@@ -862,6 +997,14 @@ type coOpCloseBalanceCheck func(t *testing.T, local, remote *HarnessNode,
862997	closeTx  * wire.MsgTx , closeUpdate  * lnrpc.ChannelCloseUpdate ,
863998	assetID , groupKey  []byte , universeTap  * tapClient )
864999
1000+ // noOpCoOpCloseBalanceCheck is a no-op implementation of the co-op close 
1001+ // balance check that can be used in tests. 
1002+ func  noOpCoOpCloseBalanceCheck (_  * testing.T , _ , _  * HarnessNode , _  * wire.MsgTx ,
1003+ 	_  * lnrpc.ChannelCloseUpdate , _ , _  []byte , _  * tapClient ) {
1004+ 
1005+ 	// This is a no-op function. 
1006+ }
1007+ 
8651008// closeAssetChannelAndAssert closes the channel between the local and remote 
8661009// node and asserts the final balances of the closing transaction. 
8671010func  closeAssetChannelAndAssert (t  * harnessTest , net  * NetworkHarness ,
0 commit comments