@@ -954,14 +954,16 @@ func testPsbtMultiSend(t *harnessTest) {
954954}
955955
956956// testMultiInputPsbtSingleAssetID tests to ensure that we can correctly
957- // construct and spend a multi-input full value PSBT where each input has the
958- // same asset ID.
957+ // construct and spend a multi-input partial value and full value PSBT where
958+ // each input has the same asset ID.
959959//
960960// The test works as follows:
961961// 1. Mint an asset on the primary tapd node.
962- // 2. Send the asset to a secondary tapd node in two different send events.
963- // 3. Send the asset back to the primary tapd node in a single multi-input PSBT
964- // send event.
962+ // 2. Send the asset to a secondary tapd node in three different send events.
963+ // 3. Send a partial amount of the asset back to the primary tapd node in a
964+ // single multi-input PSBT send event.
965+ // 4. Send the remaining amount of the asset back to the primary tapd node in
966+ // a single full value multi-input PSBT send event.
965967func testMultiInputPsbtSingleAssetID (t * harnessTest ) {
966968 var (
967969 ctxb = context .Background ()
@@ -974,6 +976,12 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) {
974976 []* mintrpc.MintAssetRequest {simpleAssets [0 ]},
975977 )
976978 rpcAsset := rpcAssets [0 ]
979+ assetTotalAmtMinted := simpleAssets [0 ].Asset .Amount
980+
981+ // The primary node asset total should be equal to the amount minted.
982+ // This variable will serve as a running total of the amount of the
983+ // asset on the primary node.
984+ primaryTapdAssetAmt := assetTotalAmtMinted
977985
978986 // Set up a node that will serve as the final multi input PSBT sender
979987 // node.
@@ -984,13 +992,16 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) {
984992 require .NoError (t .t , secondaryTapd .stop (! * noDelete ))
985993 }()
986994
987- // First of two send events from primary (minting) node to secondary
995+ // First of three send events from primary (minting) node to secondary
988996 // node.
997+ sendAmt := uint64 (1000 )
998+ changeAmt := primaryTapdAssetAmt - sendAmt
999+
9891000 genInfo := rpcAsset .AssetGenesis
9901001 addr , err := secondaryTapd .NewAddr (
9911002 ctxb , & taprpc.NewAddrRequest {
9921003 AssetId : genInfo .AssetId ,
993- Amt : 1000 ,
1004+ Amt : sendAmt ,
9941005 },
9951006 )
9961007 require .NoError (t .t , err )
@@ -1001,17 +1012,23 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) {
10011012
10021013 ConfirmAndAssertOutboundTransfer (
10031014 t .t , t .lndHarness .Miner .Client , primaryTapd , sendResp ,
1004- genInfo .AssetId , []uint64 {4000 , 1000 }, 0 , 1 ,
1015+ genInfo .AssetId , []uint64 {changeAmt , sendAmt }, 0 , 1 ,
10051016 )
10061017
10071018 AssertNonInteractiveRecvComplete (t .t , secondaryTapd , 1 )
10081019
1009- // Second of two send events from primary (minting) node to the
1010- // secondary node.
1020+ primaryTapdAssetAmt -= sendAmt
1021+
1022+ // Second of three send events from primary (minting) node to secondary
1023+ // node.
1024+ sendAmt = uint64 (1000 )
1025+ changeAmt = primaryTapdAssetAmt - sendAmt
1026+
1027+ genInfo = rpcAsset .AssetGenesis
10111028 addr , err = secondaryTapd .NewAddr (
10121029 ctxb , & taprpc.NewAddrRequest {
10131030 AssetId : genInfo .AssetId ,
1014- Amt : 4000 ,
1031+ Amt : sendAmt ,
10151032 },
10161033 )
10171034 require .NoError (t .t , err )
@@ -1022,13 +1039,47 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) {
10221039
10231040 ConfirmAndAssertOutboundTransfer (
10241041 t .t , t .lndHarness .Miner .Client , primaryTapd , sendResp ,
1025- genInfo .AssetId , []uint64 {0 , 4000 }, 1 , 2 ,
1042+ genInfo .AssetId , []uint64 {changeAmt , sendAmt }, 1 , 2 ,
10261043 )
10271044
10281045 AssertNonInteractiveRecvComplete (t .t , secondaryTapd , 2 )
10291046
1030- t .Logf ("Two separate send events complete, now attempting to send " +
1031- "back the full amount in a single multi-input PSBT send event" )
1047+ primaryTapdAssetAmt -= sendAmt
1048+
1049+ // Third of three send events from primary (minting) node to the
1050+ // secondary node.
1051+ sendAmt = uint64 (3000 )
1052+ changeAmt = primaryTapdAssetAmt - sendAmt
1053+
1054+ addr , err = secondaryTapd .NewAddr (
1055+ ctxb , & taprpc.NewAddrRequest {
1056+ AssetId : genInfo .AssetId ,
1057+ Amt : sendAmt ,
1058+ },
1059+ )
1060+ require .NoError (t .t , err )
1061+ AssertAddrCreated (t .t , secondaryTapd , rpcAsset , addr )
1062+
1063+ // Send the assets to the secondary node.
1064+ sendResp = sendAssetsToAddr (t , primaryTapd , addr )
1065+
1066+ ConfirmAndAssertOutboundTransfer (
1067+ t .t , t .lndHarness .Miner .Client , primaryTapd , sendResp ,
1068+ genInfo .AssetId , []uint64 {changeAmt , sendAmt }, 2 , 3 ,
1069+ )
1070+
1071+ AssertNonInteractiveRecvComplete (t .t , secondaryTapd , 3 )
1072+
1073+ primaryTapdAssetAmt -= sendAmt
1074+
1075+ // At this point, all three send events have completed. The primary
1076+ // node should have no assets and the secondary node should have three
1077+ // assets.
1078+ require .Equal (t .t , uint64 (0 ), primaryTapdAssetAmt )
1079+
1080+ t .Logf ("Three separate send events complete. Now attempting to send " +
1081+ "a partial amount in a single multi-input PSBT send event " +
1082+ "back to the primary node from the secondary node." )
10321083
10331084 // Ensure that the primary node has no assets before we begin.
10341085 primaryNodeAssets , err := primaryTapd .ListAssets (
@@ -1037,27 +1088,45 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) {
10371088 require .NoError (t .t , err )
10381089 require .Empty (t .t , primaryNodeAssets .Assets )
10391090
1091+ // The secondary node should have three assets as a result of the
1092+ // previous three send events.
1093+ secondaryNodeAssets , err := secondaryTapd .ListAssets (
1094+ ctxb , & taprpc.ListAssetRequest {},
1095+ )
1096+ require .NoError (t .t , err )
1097+ require .Len (t .t , secondaryNodeAssets .Assets , 3 )
1098+
10401099 // We need to derive two keys for the receiver node, one for the new
10411100 // script key and one for the internal key.
1042- receiverScriptKey , receiverAnchorIntKeyDesc := deriveKeys (
1101+ primaryNodeScriptKey , primaryNodeAnchorIntKeyDesc := deriveKeys (
10431102 t .t , primaryTapd ,
10441103 )
10451104
10461105 var assetId asset.ID
10471106 copy (assetId [:], genInfo .AssetId )
10481107
1049- var (
1050- chainParams = & address .RegressionNetTap
1051- sendAmt = uint64 (5000 )
1052- )
1108+ chainParams := & address .RegressionNetTap
1109+ sendAmt = uint64 (3500 )
1110+ changeAmt = uint64 (500 )
10531111
10541112 vPkt := tappsbt .ForInteractiveSend (
1055- assetId , sendAmt , receiverScriptKey , 0 ,
1056- receiverAnchorIntKeyDesc , asset .V0 , chainParams ,
1113+ assetId , sendAmt , primaryNodeScriptKey , 0 ,
1114+ primaryNodeAnchorIntKeyDesc , asset .V0 , chainParams ,
10571115 )
10581116
10591117 // Next, we'll attempt to fund the PSBT.
10601118 fundResp := fundPacket (t , secondaryTapd , vPkt )
1119+
1120+ // Decode and inspect the funded vPSBT.
1121+ fundedVPsbtCopy := make ([]byte , len (fundResp .FundedPsbt ))
1122+ copy (fundedVPsbtCopy , fundResp .FundedPsbt )
1123+ bytesReader := bytes .NewReader (fundedVPsbtCopy )
1124+
1125+ vPkt , err = tappsbt .NewFromRawBytes (bytesReader , false )
1126+ require .NoError (t .t , err )
1127+ require .Equal (t .t , 2 , len (vPkt .Inputs ))
1128+
1129+ // Sign the funded vPSBT.
10611130 signResp , err := secondaryTapd .SignVirtualPsbt (
10621131 ctxb , & wrpc.SignVirtualPsbtRequest {
10631132 FundedPsbt : fundResp .FundedPsbt ,
@@ -1077,20 +1146,20 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) {
10771146 var (
10781147 currentTransferIdx = 0
10791148 numTransfers = 1
1080- numOutputs = 1
1149+ numOutputs = 2
10811150 )
10821151 ConfirmAndAssertOutboundTransferWithOutputs (
10831152 t .t , t .lndHarness .Miner .Client , secondaryTapd ,
10841153 sendResp , genInfo .AssetId ,
1085- []uint64 {sendAmt }, currentTransferIdx , numTransfers ,
1154+ []uint64 {sendAmt , changeAmt }, currentTransferIdx , numTransfers ,
10861155 numOutputs ,
10871156 )
10881157
10891158 // This is an interactive transfer. Therefore, we will manually transfer
10901159 // the proof from the sender to the receiver.
10911160 _ = sendProof (
10921161 t , secondaryTapd , primaryTapd ,
1093- receiverScriptKey .PubKey .SerializeCompressed (), genInfo ,
1162+ primaryNodeScriptKey .PubKey .SerializeCompressed (), genInfo ,
10941163 )
10951164
10961165 // Finally, we make sure that the primary node has the asset.
@@ -1108,6 +1177,92 @@ func testMultiInputPsbtSingleAssetID(t *harnessTest) {
11081177 var foundAssetId asset.ID
11091178 copy (foundAssetId [:], primaryNodeAsset .AssetGenesis .AssetId )
11101179 require .Equal (t .t , assetId , foundAssetId )
1180+
1181+ t .Logf ("Partial amount multi-input PSBT send event complete. Now " +
1182+ "attempting to send the remaining amount in a full value " +
1183+ "multi-input PSBT send event back to the primary node from " +
1184+ "the secondary node." )
1185+
1186+ // Attempt a full value send of the rest of the asset back to the
1187+ // primary node.
1188+ sendAmt = uint64 (1500 )
1189+
1190+ vPkt = tappsbt .ForInteractiveSend (
1191+ assetId , sendAmt , primaryNodeScriptKey , 0 ,
1192+ primaryNodeAnchorIntKeyDesc , asset .V0 , chainParams ,
1193+ )
1194+
1195+ // Next, we'll attempt to fund the PSBT.
1196+ fundResp = fundPacket (t , secondaryTapd , vPkt )
1197+
1198+ // Decode and inspect the funded vPSBT.
1199+ fundedVPsbtCopy = make ([]byte , len (fundResp .FundedPsbt ))
1200+ copy (fundedVPsbtCopy , fundResp .FundedPsbt )
1201+ bytesReader = bytes .NewReader (fundedVPsbtCopy )
1202+
1203+ vPkt , err = tappsbt .NewFromRawBytes (bytesReader , false )
1204+ require .NoError (t .t , err )
1205+ require .Equal (t .t , 2 , len (vPkt .Inputs ))
1206+
1207+ // Sign the funded vPSBT.
1208+ signResp , err = secondaryTapd .SignVirtualPsbt (
1209+ ctxb , & wrpc.SignVirtualPsbtRequest {
1210+ FundedPsbt : fundResp .FundedPsbt ,
1211+ },
1212+ )
1213+ require .NoError (t .t , err )
1214+
1215+ // And finally anchor the PSBT in the BTC chain to complete the
1216+ // transfer.
1217+ sendResp , err = secondaryTapd .AnchorVirtualPsbts (
1218+ ctxb , & wrpc.AnchorVirtualPsbtsRequest {
1219+ VirtualPsbts : [][]byte {signResp .SignedPsbt },
1220+ },
1221+ )
1222+ require .NoError (t .t , err )
1223+
1224+ currentTransferIdx = 1
1225+ numTransfers = 2
1226+ numOutputs = 1
1227+ ConfirmAndAssertOutboundTransferWithOutputs (
1228+ t .t , t .lndHarness .Miner .Client , secondaryTapd , sendResp ,
1229+ genInfo .AssetId , []uint64 {sendAmt }, currentTransferIdx ,
1230+ numTransfers , numOutputs ,
1231+ )
1232+
1233+ // This is an interactive transfer. Therefore, we will manually transfer
1234+ // the proof from the sender to the receiver.
1235+ _ = sendProof (
1236+ t , secondaryTapd , primaryTapd ,
1237+ primaryNodeScriptKey .PubKey .SerializeCompressed (), genInfo ,
1238+ )
1239+
1240+ // Finally, we make sure that the primary node has the asset.
1241+ primaryNodeAssets , err = primaryTapd .ListAssets (
1242+ ctxb , & taprpc.ListAssetRequest {},
1243+ )
1244+ require .NoError (t .t , err )
1245+ require .Len (t .t , primaryNodeAssets .Assets , 2 )
1246+
1247+ primaryTapdAssetAmt = 0
1248+ for idx := range primaryNodeAssets .Assets {
1249+ a := primaryNodeAssets .Assets [idx ]
1250+
1251+ // Ensure matching asset ID.
1252+ copy (foundAssetId [:], a .AssetGenesis .AssetId )
1253+ require .Equal (t .t , assetId , foundAssetId )
1254+
1255+ require .True (t .t , a .Amount == 1500 || a .Amount == 3500 )
1256+ primaryTapdAssetAmt += a .Amount
1257+ }
1258+ require .Equal (t .t , primaryTapdAssetAmt , assetTotalAmtMinted )
1259+
1260+ // Finally, we ensure that the secondary node has no assets.
1261+ secondaryNodeAssets , err = secondaryTapd .ListAssets (
1262+ ctxb , & taprpc.ListAssetRequest {},
1263+ )
1264+ require .NoError (t .t , err )
1265+ require .Len (t .t , secondaryNodeAssets .Assets , 0 )
11111266}
11121267
11131268func deriveKeys (t * testing.T , tapd * tapdHarness ) (asset.ScriptKey ,
0 commit comments