@@ -1163,6 +1163,113 @@ func defaultCoOpCloseBalanceCheck(t *testing.T, local, remote *HarnessNode,
1163
1163
}
1164
1164
}
1165
1165
1166
+ // initiatorZeroAssetBalanceCoOpBalanceCheck is a co-op close balance check
1167
+ // function that can be used when the initiator has a zero asset balance.
1168
+ func initiatorZeroAssetBalanceCoOpBalanceCheck (t * testing.T , _ ,
1169
+ remote * HarnessNode , closeTx * wire.MsgTx ,
1170
+ closeUpdate * lnrpc.ChannelCloseUpdate , assetID , groupKey []byte ,
1171
+ universeTap * tapClient ) {
1172
+
1173
+ // With the channel closed, we'll now assert that the co-op close
1174
+ // transaction was inserted into the local universe.
1175
+ //
1176
+ // Since the initiator has a zero asset balance, we expect that at most
1177
+ // three outputs exist: one for the remote asset output, one for the
1178
+ // remote BTC channel balance and one for the initiator's BTC channel
1179
+ // balance (which cannot be zero or below dust due to the mandatory
1180
+ // channel reserve).
1181
+ numOutputs := 3
1182
+
1183
+ closeTxid := closeTx .TxHash ()
1184
+ require .Len (t , closeTx .TxOut , numOutputs )
1185
+
1186
+ // We assume that the local node has a non-zero BTC balance left.
1187
+ localOut , _ := closeTxOut (t , closeTx , closeUpdate , true )
1188
+ require .Greater (t , localOut .Value , int64 (1000 ))
1189
+
1190
+ // We also require there to be exactly one additional output, which is
1191
+ // the remote asset output.
1192
+ require .Len (t , closeUpdate .AdditionalOutputs , 1 )
1193
+ assetTxOut , assetOutputIndex := findTxOut (
1194
+ t , closeTx , closeUpdate .AdditionalOutputs [0 ].PkScript ,
1195
+ )
1196
+ require .LessOrEqual (t , assetTxOut .Value , int64 (1000 ))
1197
+
1198
+ // The remote node has received a couple of HTLCs with an above
1199
+ // dust value, so it should also have accumulated a non-dust
1200
+ // balance, even after subtracting 1k sats for the asset output.
1201
+ remoteCloseOut := closeUpdate .RemoteCloseOutput
1202
+ require .NotNil (t , remoteCloseOut )
1203
+
1204
+ // Find out which of the additional outputs is the local one and which
1205
+ // is the remote.
1206
+ remoteAuxOut := closeUpdate .AdditionalOutputs [0 ]
1207
+ require .False (t , remoteAuxOut .IsLocal )
1208
+
1209
+ // And then we verify the arrival of the remote balance asset proof at
1210
+ // the universe server as well.
1211
+ var remoteAssetCloseOut rfqmsg.JsonCloseOutput
1212
+ err := json .Unmarshal (
1213
+ remoteCloseOut .CustomChannelData , & remoteAssetCloseOut ,
1214
+ )
1215
+ require .NoError (t , err )
1216
+
1217
+ for assetIDStr , scriptKeyStr := range remoteAssetCloseOut .ScriptKeys {
1218
+ scriptKeyBytes , err := hex .DecodeString (scriptKeyStr )
1219
+ require .NoError (t , err )
1220
+
1221
+ require .Equal (t , hex .EncodeToString (assetID ), assetIDStr )
1222
+
1223
+ a := assertUniverseProofExists (
1224
+ t , universeTap , assetID , groupKey , scriptKeyBytes ,
1225
+ fmt .Sprintf ("%v:%v" , closeTxid , assetOutputIndex ),
1226
+ )
1227
+
1228
+ remoteTapd := newTapClient (t , remote )
1229
+
1230
+ scriptKey , err := btcec .ParsePubKey (scriptKeyBytes )
1231
+ require .NoError (t , err )
1232
+ assertAssetExists (
1233
+ t , remoteTapd , assetID , a .Amount , scriptKey , true ,
1234
+ true , false ,
1235
+ )
1236
+ }
1237
+ }
1238
+
1239
+ // closeTxOut returns either the local or remote output from the close
1240
+ // transaction, based on the information given in the close update.
1241
+ func closeTxOut (t * testing.T , closeTx * wire.MsgTx ,
1242
+ closeUpdate * lnrpc.ChannelCloseUpdate , local bool ) (* wire.TxOut , int ) {
1243
+
1244
+ var targetPkScript []byte
1245
+ if local {
1246
+ require .NotNil (t , closeUpdate .LocalCloseOutput )
1247
+ targetPkScript = closeUpdate .LocalCloseOutput .PkScript
1248
+ } else {
1249
+ require .NotNil (t , closeUpdate .RemoteCloseOutput )
1250
+ targetPkScript = closeUpdate .RemoteCloseOutput .PkScript
1251
+ }
1252
+
1253
+ return findTxOut (t , closeTx , targetPkScript )
1254
+ }
1255
+
1256
+ // findTxOut returns the transaction output with the target pk script from the
1257
+ // given transaction.
1258
+ func findTxOut (t * testing.T , tx * wire.MsgTx , targetPkScript []byte ) (
1259
+ * wire.TxOut , int ) {
1260
+
1261
+ for i , txOut := range tx .TxOut {
1262
+ if bytes .Equal (txOut .PkScript , targetPkScript ) {
1263
+ return txOut , i
1264
+ }
1265
+ }
1266
+
1267
+ t .Fatalf ("close output (targetPkScript=%x) not found in close " +
1268
+ "transaction" , targetPkScript )
1269
+
1270
+ return & wire.TxOut {}, 0
1271
+ }
1272
+
1166
1273
type tapClient struct {
1167
1274
node * HarnessNode
1168
1275
lnd * rpc.HarnessRPC
0 commit comments