@@ -953,6 +953,163 @@ func testPsbtMultiSend(t *harnessTest) {
953953 )
954954}
955955
956+ // 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.
959+ //
960+ // The test works as follows:
961+ // 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.
965+ func testMultiInputPsbtSingleAssetID (t * harnessTest ) {
966+ var (
967+ ctxb = context .Background ()
968+ primaryTapd = t .tapd
969+ )
970+
971+ // Mint a single asset.
972+ rpcAssets := MintAssetsConfirmBatch (
973+ t .t , t .lndHarness .Miner .Client , primaryTapd ,
974+ []* mintrpc.MintAssetRequest {simpleAssets [0 ]},
975+ )
976+ rpcAsset := rpcAssets [0 ]
977+
978+ // Set up a node that will serve as the final multi input PSBT sender
979+ // node.
980+ secondaryTapd := setupTapdHarness (
981+ t .t , t , t .lndHarness .Bob , t .universeServer ,
982+ )
983+ defer func () {
984+ require .NoError (t .t , secondaryTapd .stop (! * noDelete ))
985+ }()
986+
987+ // First of two send events from primary (minting) node to secondary
988+ // node.
989+ genInfo := rpcAsset .AssetGenesis
990+ addr , err := secondaryTapd .NewAddr (
991+ ctxb , & taprpc.NewAddrRequest {
992+ AssetId : genInfo .AssetId ,
993+ Amt : 1000 ,
994+ },
995+ )
996+ require .NoError (t .t , err )
997+ AssertAddrCreated (t .t , secondaryTapd , rpcAsset , addr )
998+
999+ // Send the assets to the secondary node.
1000+ sendResp := sendAssetsToAddr (t , primaryTapd , addr )
1001+
1002+ ConfirmAndAssertOutboundTransfer (
1003+ t .t , t .lndHarness .Miner .Client , primaryTapd , sendResp ,
1004+ genInfo .AssetId , []uint64 {4000 , 1000 }, 0 , 1 ,
1005+ )
1006+
1007+ AssertNonInteractiveRecvComplete (t .t , secondaryTapd , 1 )
1008+
1009+ // Second of two send events from primary (minting) node to the
1010+ // secondary node.
1011+ addr , err = secondaryTapd .NewAddr (
1012+ ctxb , & taprpc.NewAddrRequest {
1013+ AssetId : genInfo .AssetId ,
1014+ Amt : 4000 ,
1015+ },
1016+ )
1017+ require .NoError (t .t , err )
1018+ AssertAddrCreated (t .t , secondaryTapd , rpcAsset , addr )
1019+
1020+ // Send the assets to the secondary node.
1021+ sendResp = sendAssetsToAddr (t , primaryTapd , addr )
1022+
1023+ ConfirmAndAssertOutboundTransfer (
1024+ t .t , t .lndHarness .Miner .Client , primaryTapd , sendResp ,
1025+ genInfo .AssetId , []uint64 {0 , 4000 }, 1 , 2 ,
1026+ )
1027+
1028+ AssertNonInteractiveRecvComplete (t .t , secondaryTapd , 2 )
1029+
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" )
1032+
1033+ // Ensure that the primary node has no assets before we begin.
1034+ primaryNodeAssets , err := primaryTapd .ListAssets (
1035+ ctxb , & taprpc.ListAssetRequest {},
1036+ )
1037+ require .NoError (t .t , err )
1038+ require .Empty (t .t , primaryNodeAssets .Assets )
1039+
1040+ // We need to derive two keys for the receiver node, one for the new
1041+ // script key and one for the internal key.
1042+ receiverScriptKey , receiverAnchorIntKeyDesc := deriveKeys (
1043+ t .t , primaryTapd ,
1044+ )
1045+
1046+ var assetId asset.ID
1047+ copy (assetId [:], genInfo .AssetId )
1048+
1049+ var (
1050+ chainParams = & address .RegressionNetTap
1051+ sendAmt = uint64 (5000 )
1052+ )
1053+
1054+ vPkt := tappsbt .ForInteractiveSend (
1055+ assetId , sendAmt , receiverScriptKey , 0 ,
1056+ receiverAnchorIntKeyDesc , asset .V0 , chainParams ,
1057+ )
1058+
1059+ // Next, we'll attempt to fund the PSBT.
1060+ fundResp := fundPacket (t , secondaryTapd , vPkt )
1061+ signResp , err := secondaryTapd .SignVirtualPsbt (
1062+ ctxb , & wrpc.SignVirtualPsbtRequest {
1063+ FundedPsbt : fundResp .FundedPsbt ,
1064+ },
1065+ )
1066+ require .NoError (t .t , err )
1067+
1068+ // And finally anchor the PSBT in the BTC chain to complete the
1069+ // transfer.
1070+ sendResp , err = secondaryTapd .AnchorVirtualPsbts (
1071+ ctxb , & wrpc.AnchorVirtualPsbtsRequest {
1072+ VirtualPsbts : [][]byte {signResp .SignedPsbt },
1073+ },
1074+ )
1075+ require .NoError (t .t , err )
1076+
1077+ var (
1078+ currentTransferIdx = 0
1079+ numTransfers = 1
1080+ numOutputs = 1
1081+ )
1082+ ConfirmAndAssetOutboundTransferWithOutputs (
1083+ t .t , t .lndHarness .Miner .Client , secondaryTapd ,
1084+ sendResp , genInfo .AssetId ,
1085+ []uint64 {sendAmt }, currentTransferIdx , numTransfers ,
1086+ numOutputs ,
1087+ )
1088+
1089+ // This is an interactive transfer. Therefore, we will manually transfer
1090+ // the proof from the sender to the receiver.
1091+ _ = sendProof (
1092+ t , secondaryTapd , primaryTapd ,
1093+ receiverScriptKey .PubKey .SerializeCompressed (), genInfo ,
1094+ )
1095+
1096+ // Finally, we make sure that the primary node has the asset.
1097+ primaryNodeAssets , err = primaryTapd .ListAssets (
1098+ ctxb , & taprpc.ListAssetRequest {},
1099+ )
1100+ require .NoError (t .t , err )
1101+ require .Len (t .t , primaryNodeAssets .Assets , 1 )
1102+
1103+ // Ensure that the asset is the one we expect.
1104+ primaryNodeAsset := primaryNodeAssets .Assets [0 ]
1105+
1106+ require .Equal (t .t , primaryNodeAsset .Amount , sendAmt )
1107+
1108+ var foundAssetId asset.ID
1109+ copy (foundAssetId [:], primaryNodeAsset .AssetGenesis .AssetId )
1110+ require .Equal (t .t , assetId , foundAssetId )
1111+ }
1112+
9561113func deriveKeys (t * testing.T , tapd * tapdHarness ) (asset.ScriptKey ,
9571114 keychain.KeyDescriptor ) {
9581115
0 commit comments